Skip to content

Commit

Permalink
support redission json & jsonb codec, for issue #2420
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Apr 13, 2024
1 parent 444b461 commit 86cf4ec
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 0 deletions.
4 changes: 4 additions & 0 deletions core/src/main/java/com/alibaba/fastjson2/JSONReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -3229,6 +3229,10 @@ public static JSONReader ofJSONB(byte[] jsonbBytes, JSONReader.Context context)
jsonbBytes.length);
}

public static JSONReader ofJSONB(InputStream in, JSONReader.Context context) {
return new JSONReaderJSONB(context, in);
}

public static JSONReader ofJSONB(byte[] jsonbBytes, JSONReader.Feature... features) {
Context context = JSONFactory.createReadContext();
context.config(features);
Expand Down
6 changes: 6 additions & 0 deletions extension/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
<artifactId>arrow-vector</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.28.0</version>
<scope>provided</scope>
</dependency>

<!-- test libs -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.alibaba.fastjson2.support.redission;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.writer.ObjectWriter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;

import java.io.IOException;
import java.lang.reflect.Type;

public class JSONBCodec
extends BaseCodec {
final JSONEncoder encoder;
final JSONDecoder decoder;

public JSONBCodec(Type valueType) {
this(JSONFactory.createWriteContext(), JSONFactory.createReadContext(), valueType);
}

public JSONBCodec(JSONWriter.Context writerContext, JSONReader.Context readerContext) {
this(writerContext, readerContext, null);
}

public JSONBCodec(JSONWriter.Context writerContext, JSONReader.Context readerContext, Type valueType) {
this(new JSONEncoder(writerContext), new JSONDecoder(readerContext, valueType));
}

public JSONBCodec(JSONEncoder encoder, JSONDecoder decoder) {
this.encoder = encoder;
this.decoder = decoder;
}

@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}

@Override
public Encoder getValueEncoder() {
return encoder;
}

public static class JSONEncoder
implements Encoder {
final JSONWriter.Context context;

public JSONEncoder(JSONWriter.Context context) {
this.context = context;
}

@Override
public ByteBuf encode(Object object) throws IOException {
try (JSONWriter writer = JSONWriter.ofJSONB(context)) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);

Class<?> valueClass = object.getClass();
if (valueClass == JSONObject.class && context.getFeatures() == 0) {
writer.write((JSONObject) object);
} else {
ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass);
objectWriter.write(writer, object, null, null, 0);
}
}

int size = writer.size();
ByteBuf out = ByteBufAllocator.DEFAULT.buffer(size);
ByteBufOutputStream baos = new ByteBufOutputStream(out);
writer.flushTo(baos);
return baos.buffer();
} catch (NullPointerException | NumberFormatException e) {
throw new JSONException("JSON#toJSONString cannot serialize '" + object + "'", e);
}
}
}

public static class JSONDecoder
implements Decoder<Object> {
final JSONReader.Context context;
final Type valueType;

public JSONDecoder(JSONReader.Context context, Type valueType) {
this.context = context;
this.valueType = valueType;
}

@Override
public Object decode(ByteBuf buf, State state) throws IOException {
Object value;
try (JSONReader jsonReader = JSONReader.ofJSONB(new ByteBufInputStream(buf), context)) {
if (valueType == null || valueType == Object.class) {
value = jsonReader.readAny();
} else {
value = jsonReader.read(valueType);
}
}
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.alibaba.fastjson2.support.redission;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.writer.ObjectWriter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class JSONCodec
extends BaseCodec {
final JSONEncoder encoder;
final JSONDecoder decoder;

public JSONCodec(Type type) {
this(JSONFactory.createWriteContext(), JSONFactory.createReadContext(), type, StandardCharsets.UTF_8);
}

public JSONCodec(Type type, Charset charset) {
this(JSONFactory.createWriteContext(), JSONFactory.createReadContext(), type, charset);
}

public JSONCodec(JSONWriter.Context writerContext, JSONReader.Context readerContext) {
this(writerContext, readerContext, null, StandardCharsets.UTF_8);
}

public JSONCodec(JSONWriter.Context writerContext, JSONReader.Context readerContext, Charset charset) {
this(writerContext, readerContext, null, charset);
}

public JSONCodec(JSONWriter.Context writerContext, JSONReader.Context readerContext, Type valueType, Charset charset) {
this(new JSONEncoder(writerContext, charset), new JSONDecoder(valueType, charset, readerContext));
}

public JSONCodec(JSONEncoder encoder, JSONDecoder decoder) {
this.encoder = encoder;
this.decoder = decoder;
}

@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}

@Override
public Encoder getValueEncoder() {
return encoder;
}

public static class JSONEncoder
implements Encoder {
final JSONWriter.Context context;
final Charset charset;

public JSONEncoder() {
this(new JSONWriter.Context());
}

public JSONEncoder(JSONWriter.Context context) {
this(context, StandardCharsets.UTF_8);
}

public JSONEncoder(JSONWriter.Context context, Charset charset) {
this.context = context;
this.charset = charset;
}

@Override
public ByteBuf encode(Object object) throws IOException {
try (JSONWriter writer = JSONWriter.of(context)) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);

Class<?> valueClass = object.getClass();
if (valueClass == JSONObject.class && context.getFeatures() == 0) {
writer.write((JSONObject) object);
} else {
ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass);
objectWriter.write(writer, object, null, null, 0);
}
}

int size = writer.size();
if (!context.isEnabled(JSONWriter.Feature.OptimizedForAscii)) {
size <<= 2;
}
ByteBuf out = ByteBufAllocator.DEFAULT.buffer(size);
ByteBufOutputStream baos = new ByteBufOutputStream(out);
writer.flushTo(baos, charset);
return baos.buffer();
} catch (NullPointerException | NumberFormatException e) {
throw new JSONException("JSON#toJSONString cannot serialize '" + object + "'", e);
}
}
}

public static class JSONDecoder
implements Decoder<Object> {
final Type valueType;
final Charset charset;
final JSONReader.Context context;

public JSONDecoder(Type valueType) {
this(valueType, JSONFactory.createReadContext());
}

public JSONDecoder(Type valueType, JSONReader.Context context) {
this(valueType, StandardCharsets.UTF_8, context);
}

public JSONDecoder(Type valueType, Charset charset, JSONReader.Context context) {
this.valueType = valueType;
this.charset = charset;
this.context = context;
}

@Override
public Object decode(ByteBuf buf, State state) throws IOException {
Object value;
try (JSONReader jsonReader = JSONReader.of(new ByteBufInputStream(buf), charset, context)) {
if (valueType == null || valueType == Object.class) {
value = jsonReader.readAny();
} else {
value = jsonReader.read(valueType);
}
}
return value;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.alibaba.fastjson2.support;

import com.alibaba.fastjson2.JSONFactory;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.support.redission.JSONBCodec;
import com.alibaba.fastjson2.support.redission.JSONCodec;
import io.netty.buffer.ByteBuf;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class JSONCodecTest {
@Test
public void json() throws Exception {
JSONCodec codec = new JSONCodec(Bean.class);

Bean bean = new Bean();
bean.name = "abc";

ByteBuf encoded = codec.getValueEncoder()
.encode(bean);

Bean decoded = (Bean) codec.getValueDecoder().decode(encoded, null);
assertEquals(bean.name, decoded.name);
}

@Test
public void jsonAutoType() throws Exception {
JSONCodec codec = new JSONCodec(
JSONFactory.createWriteContext(JSONWriter.Feature.WriteClassName),
JSONFactory.createReadContext(JSONReader.autoTypeFilter(Bean.class))
);

Bean bean = new Bean();
bean.name = "abc";

ByteBuf encoded = codec.getValueEncoder()
.encode(bean);

Bean decoded = (Bean) codec.getValueDecoder().decode(encoded, null);
assertEquals(bean.name, decoded.name);
}

@Test
public void jsonb() throws Exception {
JSONBCodec codec = new JSONBCodec(Bean.class);

Bean bean = new Bean();
bean.name = "abc";

ByteBuf encoded = codec.getValueEncoder()
.encode(bean);

Bean decoded = (Bean) codec.getValueDecoder().decode(encoded, null);
assertEquals(bean.name, decoded.name);
}

@Test
public void jsonbAutoType() throws Exception {
JSONBCodec codec = new JSONBCodec(
JSONFactory.createWriteContext(JSONWriter.Feature.WriteClassName),
JSONFactory.createReadContext(JSONReader.autoTypeFilter(Bean.class))
);

Bean bean = new Bean();
bean.name = "abc";

ByteBuf encoded = codec.getValueEncoder()
.encode(bean);

Bean decoded = (Bean) codec.getValueDecoder()
.decode(encoded, null);
assertEquals(bean.name, decoded.name);
}

public static class Bean {
public String name;
}
}

0 comments on commit 86cf4ec

Please sign in to comment.