diff --git a/example-solon-test/README.md b/example-solon-test/README.md new file mode 100644 index 000000000..8ad8f8f65 --- /dev/null +++ b/example-solon-test/README.md @@ -0,0 +1,20 @@ +### 启动方式 + +运行 SolonTestApp.main 方法(或者直接运行单测 DemoTest ) + +### 测试 + +测试 Render 是否正常工作 + +* 访问:[http://localhost:8080/demo?username=world&password=1234](http://localhost:8080/demo?username=world&password=1234) + +测试 找不到的地址异常(技术上和上面一样,Solon 所有的 Web 输出都会走:Render 接口) + +* 访问:[http://localhost:8080/error](http://localhost:8080/error) + +### 说明 + +此演示,会自动触发 fastjson2-extension-solon 的: + +* Fastjson2RenderFactory +* Fastjson2ActionExecutor diff --git a/example-solon-test/pom.xml b/example-solon-test/pom.xml new file mode 100644 index 000000000..9eace9d1e --- /dev/null +++ b/example-solon-test/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + com.alibaba.fastjson2 + fastjson2-parent + 2.0.54-SNAPSHOT + ../pom.xml + + + com.com.alibaba.fastjson2 + example-solon-test + example-solon-test + example-solon-test + + + 8 + true + true + + + + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + + snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + + + + + org.noear + solon-parent + pom + ${solon.version} + import + + + + + + + com.alibaba.fastjson2 + fastjson2-extension-solon + ${project.version} + + + + org.noear + solon-boot-jdkhttp + + + + org.noear + solon-logging-simple + + + + org.projectlombok + lombok + provided + + + + org.noear + solon-test + test + + + + + + + org.noear + solon-maven-plugin + ${solon.version} + + + package + + repackage + + + + + + + diff --git a/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/SolonTestApp.java b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/SolonTestApp.java new file mode 100644 index 000000000..8c504bec6 --- /dev/null +++ b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/SolonTestApp.java @@ -0,0 +1,11 @@ +package com.alibaba.fastjson2.example.solontest; + +import org.noear.solon.Solon; +import org.noear.solon.annotation.SolonMain; + +@SolonMain +public class SolonTestApp { + public static void main(String[] args) { + Solon.start(SolonTestApp.class, args); + } +} diff --git a/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/config/JsonConfigurer.java b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/config/JsonConfigurer.java new file mode 100644 index 000000000..2b475a82e --- /dev/null +++ b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/config/JsonConfigurer.java @@ -0,0 +1,24 @@ +package com.alibaba.fastjson2.example.solontest.config; + +import com.alibaba.fastjson2.support.solon.Fastjson2ActionExecutor; +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import org.noear.solon.annotation.Bean; +import org.noear.solon.annotation.Configuration; + +/** + * @author noear + * @since 2024-10-01 + */ +@Configuration +public class JsonConfigurer { + @Bean + public void fastjson2(Fastjson2ActionExecutor executor, Fastjson2RenderFactory render) { +// executor.config().config( +// JSONReader.Feature.FieldBased, +// JSONReader.Feature.SupportArrayToBean); +// +// render.addFeatures( +// JSONWriter.Feature.WriteMapNullValue, +// JSONWriter.Feature.PrettyFormat); + } +} diff --git a/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoController.java b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoController.java new file mode 100644 index 000000000..3abad45ac --- /dev/null +++ b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoController.java @@ -0,0 +1,17 @@ +package com.alibaba.fastjson2.example.solontest.controller; + +import com.alibaba.fastjson2.example.solontest.entity.User; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; + +/** + * @author noear + * @since 2024-10-01 + */ +@Controller +public class DemoController { + @Mapping("/demo") + public User demo(User user) { + return user; + } +} diff --git a/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoErrorFilter.java b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoErrorFilter.java new file mode 100644 index 000000000..924253782 --- /dev/null +++ b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/controller/DemoErrorFilter.java @@ -0,0 +1,27 @@ +package com.alibaba.fastjson2.example.solontest.controller; + +import org.noear.solon.annotation.Component; +import org.noear.solon.core.exception.StatusException; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; +import org.noear.solon.core.handle.Result; + +/** + * @author noear + * @since 2024-10-01 + */ +@Component +public class DemoErrorFilter + implements Filter { + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + try { + chain.doFilter(ctx); + } catch (StatusException e) { + ctx.render(Result.failure(e.getCode(), e.getMessage())); + } catch (Throwable e) { + ctx.render(Result.failure(500)); + } + } +} diff --git a/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/entity/User.java b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/entity/User.java new file mode 100644 index 000000000..f32f80b4a --- /dev/null +++ b/example-solon-test/src/main/java/com/alibaba/fastjson2/example/solontest/entity/User.java @@ -0,0 +1,17 @@ +package com.alibaba.fastjson2.example.solontest.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author noear + * @since 2024-10-01 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class User { + private String username; + private String password; +} diff --git a/example-solon-test/src/main/resources/app.properties b/example-solon-test/src/main/resources/app.properties new file mode 100644 index 000000000..0a0a9762f --- /dev/null +++ b/example-solon-test/src/main/resources/app.properties @@ -0,0 +1,3 @@ +server.port=8080 + +solon.logging.logger.root.level=INFO diff --git a/example-solon-test/src/test/java/com/alibaba/fastjson2/example/solontest/DemoTest.java b/example-solon-test/src/test/java/com/alibaba/fastjson2/example/solontest/DemoTest.java new file mode 100644 index 000000000..f5117bb3d --- /dev/null +++ b/example-solon-test/src/test/java/com/alibaba/fastjson2/example/solontest/DemoTest.java @@ -0,0 +1,39 @@ +package com.alibaba.fastjson2.example.solontest; + +import org.junit.jupiter.api.Test; +import org.noear.solon.test.HttpTester; +import org.noear.solon.test.SolonTest; + +/** + * @author noear 2024/10/2 created + */ +@SolonTest(SolonTestApp.class) +public class DemoTest + extends HttpTester { + @Test + public void ok_post_json() throws Exception { + String json = "{\"password\":\"1234\",\"username\":\"world\"}"; + + String json2 = path("/demo").bodyJson(json).post(); + + assert json.equals(json2); + } + + @Test + public void ok_get() throws Exception { + String json = "{\"password\":\"1234\",\"username\":\"world\"}"; + + String json2 = path("/demo?username=world&password=1234").get(); + + assert json.equals(json2); + } + + @Test + public void error() throws Exception { + String json = "{\"code\":404,\"description\":\"Not Found: GET /error\"}"; + + String json2 = path("/error").get(); + + assert json.equals(json2); + } +} diff --git a/extension-solon/pom.xml b/extension-solon/pom.xml new file mode 100644 index 000000000..bf592af34 --- /dev/null +++ b/extension-solon/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + com.alibaba.fastjson2 + fastjson2-parent + 2.0.54-SNAPSHOT + ../pom.xml + + + fastjson2-extension-solon + fastjson2-extension-solon + Fastjson is a JSON processor (JSON parser + JSON generator) written in Java + jar + https://github.com/alibaba/fastjson2 + 2024 + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + + + https://github.com/alibaba/fastjson2 + scm:git:https://git@github.com/alibaba/fastjson2.git + + + Alibaba Group + https://github.com/alibaba + + + + wenshao + wenshao + shaojin.wensj(at)alibaba-inc.com + + Developer + Tech Leader + + +8 + https://github.com/wenshao + + + noear + noear + noear@live.cn + +8 + https://github.com/noear + + + + + + com.alibaba.fastjson2 + fastjson2 + ${project.version} + + + + org.noear + solon-serialization + ${solon.version} + + + + org.noear + solon-logging-simple + ${solon.version} + test + + + + org.noear + solon-test + ${solon.version} + test + + + + + + + maven-surefire-plugin + + + com/alibaba/fastjson2/**/*.java + + + Asia/Shanghai + + + + + + diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2ActionExecutor.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2ActionExecutor.java new file mode 100644 index 000000000..1ce8e7463 --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2ActionExecutor.java @@ -0,0 +1,149 @@ +package com.alibaba.fastjson2.support.solon; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.mvc.ActionExecuteHandlerDefault; +import org.noear.solon.core.wrap.MethodWrap; +import org.noear.solon.core.wrap.ParamWrap; + +import java.util.Collection; +import java.util.List; + +/** + * Json ActionExecuteHandler + * + * @author noear + * @author 夜の孤城 + * @author 暮城留风 + * @since 1.9 + * @since 2024-10-01 + * */ +public class Fastjson2ActionExecutor + extends ActionExecuteHandlerDefault { + private final Fastjson2StringSerializer serializer = new Fastjson2StringSerializer(); + + public Fastjson2ActionExecutor() { + serializer.getDeserializeConfig().config(); + serializer.getDeserializeConfig().config(JSONReader.Feature.ErrorOnEnumNotMatch); + } + + /** + * Gets the serialization interface + */ + public Fastjson2StringSerializer getSerializer() { + return serializer; + } + + /** + * Deserialize the configuration + */ + public JSONReader.Context config() { + return getSerializer().getDeserializeConfig(); + } + + /** + * Match or not + * + * @param ctx Handling context + * @param mime Content type + */ + @Override + public boolean matched(Context ctx, String mime) { + return serializer.matched(ctx, mime); + } + + /** + * Converting body + * + * @param ctx Handling context + * @param mWrap Method wrappers + */ + @Override + protected Object changeBody(Context ctx, MethodWrap mWrap) throws Exception { + return serializer.deserializeFromBody(ctx); + } + + /** + * 转换 value + * + * @param ctx Handling context + * @param p Parameter wrappers + * @param pi Parameter index + * @param pt Parameter type + * @param bodyObj Body object + */ + @Override + protected Object changeValue(Context ctx, ParamWrap p, int pi, Class pt, Object bodyObj) throws Exception { + if (p.spec().isRequiredPath() || p.spec().isRequiredCookie() || p.spec().isRequiredHeader()) { + //If path、cookie, header? + return super.changeValue(ctx, p, pi, pt, bodyObj); + } + + if (p.spec().isRequiredBody() == false && ctx.paramMap().containsKey(p.spec().getName())) { + //If path、queryString? + return super.changeValue(ctx, p, pi, pt, bodyObj); + } + + if (bodyObj == null) { + return super.changeValue(ctx, p, pi, pt, bodyObj); + } + + if (bodyObj instanceof JSONObject) { + JSONObject tmp = (JSONObject) bodyObj; + + if (p.spec().isRequiredBody() == false) { + // + //If there is no body requirement; Try to find by attribute + // + if (tmp.containsKey(p.spec().getName())) { + //Supports conversions of generic types + if (p.spec().isGenericType()) { + return tmp.getObject(p.spec().getName(), p.getGenericType()); + } else { + return tmp.getObject(p.spec().getName(), pt); + } + } + } + + //Try the body conversion + if (pt.isPrimitive() || pt.getTypeName().startsWith("java.lang.")) { + return super.changeValue(ctx, p, pi, pt, bodyObj); + } else { + if (List.class.isAssignableFrom(pt)) { + return null; + } + + if (pt.isArray()) { + return null; + } + + //Generic transformations such as Map + if (p.spec().isGenericType()) { + return tmp.to(p.getGenericType()); + } else { + return tmp.to(pt); + } + } + } + + if (bodyObj instanceof JSONArray) { + JSONArray tmp = (JSONArray) bodyObj; + //If the argument is a non-collection type + if (!Collection.class.isAssignableFrom(pt)) { + return null; + } + //Collection Type Conversions + if (p.spec().isGenericType()) { + //Transforms a collection with generics + return tmp.to(p.getGenericType()); + } else { + //Can be converted not only to a List but also to a Set + return tmp.to(pt); + } + } + + return bodyObj; + } +} diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactory.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactory.java new file mode 100644 index 000000000..d744abf35 --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactory.java @@ -0,0 +1,113 @@ +package com.alibaba.fastjson2.support.solon; + +import com.alibaba.fastjson2.JSONWriter; +import org.noear.solon.core.handle.Render; +import org.noear.solon.serialization.StringSerializerRender; +import org.noear.solon.serialization.prop.JsonProps; +import org.noear.solon.serialization.prop.JsonPropsUtil; + +/** + * Json RenderFactory + * + * @author noear + * @author 暮城留风 + * @since 1.10 + * @since 2024-10-01 + */ +public class Fastjson2RenderFactory + extends Fastjson2RenderFactoryBase { + public Fastjson2RenderFactory(JsonProps jsonProps) { + serializer.cfgSerializeFeatures(false, true, + JSONWriter.Feature.BrowserCompatible); + applyProps(jsonProps); + } + + /** + * Suffix or name mapping + */ + @Override + public String[] mappings() { + return new String[]{"@json"}; + } + + /** + * Create Render + */ + @Override + public Render create() { + return new StringSerializerRender(false, serializer); + } + + /** + * Resetting features + */ + public void setFeatures(JSONWriter.Feature... features) { + serializer.cfgSerializeFeatures(true, true, features); + } + + /** + * Adding features + */ + public void addFeatures(JSONWriter.Feature... features) { + serializer.cfgSerializeFeatures(false, true, features); + } + + /** + * Removing features + */ + public void removeFeatures(JSONWriter.Feature... features) { + serializer.cfgSerializeFeatures(false, false, features); + } + + protected void applyProps(JsonProps jsonProps) { + if (jsonProps != null && jsonProps.dateAsTicks) { + jsonProps.dateAsTicks = false; + this.getSerializer().getSerializeConfig() + .setDateFormat("millis"); + } + + if (JsonPropsUtil.apply(this, jsonProps)) { + if (jsonProps.longAsString) { + this.addFeatures(JSONWriter.Feature.WriteLongAsString); + } + + boolean writeNulls = jsonProps.nullAsWriteable || + jsonProps.nullNumberAsZero || + jsonProps.nullArrayAsEmpty || + jsonProps.nullBoolAsFalse || + jsonProps.nullStringAsEmpty; + + if (jsonProps.nullStringAsEmpty) { + this.addFeatures(JSONWriter.Feature.WriteNullStringAsEmpty); + } + + if (jsonProps.nullBoolAsFalse) { + this.addFeatures(JSONWriter.Feature.WriteNullBooleanAsFalse); + } + + if (jsonProps.nullNumberAsZero) { + this.addFeatures(JSONWriter.Feature.WriteNullNumberAsZero); + } + + if (jsonProps.boolAsInt) { + this.addFeatures(JSONWriter.Feature.WriteBooleanAsNumber); + } + + if (jsonProps.longAsString) { + this.addFeatures(JSONWriter.Feature.WriteLongAsString); + } + + if (jsonProps.nullArrayAsEmpty) { + this.addFeatures(JSONWriter.Feature.WriteNullListAsEmpty); + } + + if (jsonProps.enumAsName) { + this.addFeatures(JSONWriter.Feature.WriteEnumsUsingName); + } + + if (writeNulls) { + this.addFeatures(JSONWriter.Feature.WriteNulls); + } + } + } +} diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactoryBase.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactoryBase.java new file mode 100644 index 000000000..c82b029be --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderFactoryBase.java @@ -0,0 +1,78 @@ +package com.alibaba.fastjson2.support.solon; + +import com.alibaba.fastjson2.writer.ObjectWriter; +import com.alibaba.fastjson2.writer.ObjectWriterProvider; +import org.noear.solon.core.convert.Converter; +import org.noear.solon.serialization.JsonRenderFactory; + +/** + * Json RenderFactory Base + * + * @author noear + * @author 暮城留风 + * @since 1.10 + * @since 2024-10-01 + */ +public abstract class Fastjson2RenderFactoryBase + implements JsonRenderFactory { + protected Fastjson2StringSerializer serializer = new Fastjson2StringSerializer(); + + public Fastjson2RenderFactoryBase() { + //The default time handling is a timestamp + serializer.getSerializeConfig().setDateFormat("millis"); + } + + /** + * Gets the serializer + */ + public Fastjson2StringSerializer getSerializer() { + return serializer; + } + + /** + * Serialize the configuration + */ + public ObjectWriterProvider config() { + return serializer.getSerializeConfig().getProvider(); + } + + /** + * Adding the encoder + * + * @param clz type + * @param encoder encoder + */ + public void addEncoder(Class clz, ObjectWriter encoder) { + config().register(clz, encoder); + } + + /** + * Add converter (simplified version of encoder) + * + * @param clz type + * @param converter converter + */ + @Override + public void addConvertor(Class clz, Converter converter) { + addEncoder(clz, (out, obj, fieldName, fieldType, features) -> { + Object val = converter.convert((T) obj); + if (val == null) { + out.writeNull(); + } else if (val instanceof String) { + out.writeString((String) val); + } else if (val instanceof Number) { + if (val instanceof Long) { + out.writeInt64(((Number) val).longValue()); + } else if (val instanceof Integer) { + out.writeInt32(((Number) val).intValue()); + } else if (val instanceof Float) { + out.writeDouble(((Number) val).floatValue()); + } else { + out.writeDouble(((Number) val).doubleValue()); + } + } else { + throw new IllegalArgumentException("The result type of the converter is not supported: " + val.getClass().getName()); + } + }); + } +} diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderTypedFactory.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderTypedFactory.java new file mode 100644 index 000000000..a1579d0a4 --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2RenderTypedFactory.java @@ -0,0 +1,41 @@ +package com.alibaba.fastjson2.support.solon; + +import com.alibaba.fastjson2.JSONWriter; +import org.noear.solon.core.handle.Render; +import org.noear.solon.serialization.StringSerializerRender; + +/** + * Json Typed RenderFactory (Typically used with RPC) + * + * @author noear + * @author 暮城留风 + * @since 1.10 + * @since 2024-10-01 + */ +public class Fastjson2RenderTypedFactory + extends Fastjson2RenderFactoryBase { + public Fastjson2RenderTypedFactory() { + serializer.cfgSerializeFeatures(false, true, + JSONWriter.Feature.BrowserCompatible, + JSONWriter.Feature.WriteClassName, + JSONWriter.Feature.ReferenceDetection, + JSONWriter.Feature.WriteLongAsString + ); + } + + /** + * Suffix or name mapping + */ + @Override + public String[] mappings() { + return new String[]{"@type_json"}; + } + + /** + * Create Render + */ + @Override + public Render create() { + return new StringSerializerRender(true, serializer); + } +} diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2StringSerializer.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2StringSerializer.java new file mode 100644 index 000000000..f2c69ec4e --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/Fastjson2StringSerializer.java @@ -0,0 +1,176 @@ +package com.alibaba.fastjson2.support.solon; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONFactory; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.reader.ObjectReaderProvider; +import com.alibaba.fastjson2.writer.ObjectWriterProvider; +import org.noear.solon.Utils; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.ModelAndView; +import org.noear.solon.serialization.ContextSerializer; + +import java.io.IOException; +import java.lang.reflect.Type; + +/** + * Fastjson2 string serialization + * + * @author noear + * @author 暮城留风 + * @since 1.10 + * @since 2.8 + * @since 2024-10-01 + */ +public class Fastjson2StringSerializer + implements ContextSerializer { + private static final String label = "/json"; + + private JSONWriter.Context serializeConfig; + private JSONReader.Context deserializeConfig; + + /** + * Get the serialization configuration + */ + public JSONWriter.Context getSerializeConfig() { + if (serializeConfig == null) { + serializeConfig = new JSONWriter.Context(new ObjectWriterProvider()); + } + + return serializeConfig; + } + + /** + * Configure the serialization feature + * + * @param isReset Reset or not + * @param isAdd Add or not + * @param features Feature + */ + public void cfgSerializeFeatures(boolean isReset, boolean isAdd, JSONWriter.Feature... features) { + if (isReset) { + getSerializeConfig().setFeatures(JSONFactory.getDefaultWriterFeatures()); + } + + for (JSONWriter.Feature feature : features) { + getSerializeConfig().config(feature, isAdd); + } + } + + /** + * Get the deserialized configuration + */ + public JSONReader.Context getDeserializeConfig() { + if (deserializeConfig == null) { + deserializeConfig = new JSONReader.Context(new ObjectReaderProvider()); + } + return deserializeConfig; + } + + /** + * Configure the deserialization feature + * + * @param isReset Reset or not + * @param isAdd Add or not + * @param features Feature + */ + public void cfgDeserializeFeatures(boolean isReset, boolean isAdd, JSONReader.Feature... features) { + if (isReset) { + getDeserializeConfig().setFeatures(JSONFactory.getDefaultReaderFeatures()); + } + + for (JSONReader.Feature feature : features) { + getDeserializeConfig().config(feature, isAdd); + } + } + + /** + * Getting the content type + */ + @Override + public String getContentType() { + return "application/json"; + } + + /** + * Match or not + * + * @param ctx Handling context + * @param mime content type + */ + @Override + public boolean matched(Context ctx, String mime) { + if (mime == null) { + return false; + } else { + return mime.contains(label); + } + } + + /** + * Serializer name + */ + @Override + public String name() { + return "fastjson2-json"; + } + + /** + * Serialize + * + * @param obj object + */ + @Override + public String serialize(Object obj) throws IOException { + return JSON.toJSONString(obj, getSerializeConfig()); + } + + /** + * Deserialize + * + * @param data data + * @param toType Target type + */ + @Override + public Object deserialize(String data, Type toType) throws IOException { + if (toType == null) { + return JSON.parse(data, getDeserializeConfig()); + } else { + return JSON.parseObject(data, toType, getDeserializeConfig()); + } + } + + /** + * Serialize the body + * + * @param ctx Handling context + * @param data data + */ + @Override + public void serializeToBody(Context ctx, Object data) throws IOException { + ctx.contentType(getContentType()); + + if (data instanceof ModelAndView) { + ctx.output(serialize(((ModelAndView) data).model())); + } else { + ctx.output(serialize(data)); + } + } + + /** + * Deserialize the body + * + * @param ctx Handling context + */ + @Override + public Object deserializeFromBody(Context ctx) throws IOException { + String data = ctx.bodyNew(); + + if (Utils.isNotEmpty(data)) { + return JSON.parse(data, getDeserializeConfig()); + } else { + return null; + } + } +} diff --git a/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/integration/Fastjson2Plugin.java b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/integration/Fastjson2Plugin.java new file mode 100644 index 000000000..8c8a90c2b --- /dev/null +++ b/extension-solon/src/main/java/com/alibaba/fastjson2/support/solon/integration/Fastjson2Plugin.java @@ -0,0 +1,37 @@ +package com.alibaba.fastjson2.support.solon.integration; + +import com.alibaba.fastjson2.support.solon.Fastjson2ActionExecutor; +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.Fastjson2RenderTypedFactory; +import org.noear.solon.Solon; +import org.noear.solon.core.AppContext; +import org.noear.solon.core.Plugin; +import org.noear.solon.serialization.prop.JsonProps; + +/** + * Fastjson2 for solon extension integration plugin + * + * @author noear + * */ +public class Fastjson2Plugin + implements Plugin { + @Override + public void start(AppContext context) { + JsonProps jsonProps = JsonProps.create(context); + + //::renderFactory + Fastjson2RenderFactory renderFactory = new Fastjson2RenderFactory(jsonProps); //绑定属性 + context.wrapAndPut(Fastjson2RenderFactory.class, renderFactory); //推入容器,用于扩展 + Solon.app().renderManager().register(renderFactory); + + //::renderTypedFactory + Fastjson2RenderTypedFactory renderTypedFactory = new Fastjson2RenderTypedFactory(); + context.wrapAndPut(Fastjson2RenderTypedFactory.class, renderTypedFactory); //推入容器,用于扩展 + Solon.app().renderManager().register(renderTypedFactory); + + //::actionExecutor + Fastjson2ActionExecutor actionExecutor = new Fastjson2ActionExecutor(); //支持 json 内容类型执行 + context.wrapAndPut(Fastjson2ActionExecutor.class, actionExecutor); //推入容器,用于扩展 + Solon.app().chainManager().addExecuteHandler(actionExecutor); + } +} diff --git a/extension-solon/src/main/resources/META-INF/solon/fastjson2-extension-solon.properties b/extension-solon/src/main/resources/META-INF/solon/fastjson2-extension-solon.properties new file mode 100644 index 000000000..85d79f712 --- /dev/null +++ b/extension-solon/src/main/resources/META-INF/solon/fastjson2-extension-solon.properties @@ -0,0 +1,2 @@ +solon.plugin=com.alibaba.fastjson2.support.solon.integration.Fastjson2Plugin +solon.plugin.priority=18 diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/CustomDateDo.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/CustomDateDo.java new file mode 100644 index 000000000..f0425be6a --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/CustomDateDo.java @@ -0,0 +1,19 @@ +package com.alibaba.fastjson2.support.solon.test._model; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; + +/** + * @author noear 2024/9/4 created + */ +@Setter +@Getter +public class CustomDateDo { + private Date date; + + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private Date date2; +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/OrderDo.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/OrderDo.java new file mode 100644 index 000000000..8ec342f94 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/OrderDo.java @@ -0,0 +1,16 @@ +package com.alibaba.fastjson2.support.solon.test._model; + +/** + * @author noear 2023/8/16 created + */ +public class OrderDo { + long orderId = 2; + + public void setOrderId(long orderId) { + this.orderId = orderId; + } + + public long getOrderId() { + return orderId; + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/UserDo.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/UserDo.java new file mode 100644 index 000000000..0fd9a75c9 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/_model/UserDo.java @@ -0,0 +1,34 @@ +package com.alibaba.fastjson2.support.solon.test._model; + +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Getter +@Setter +public class UserDo + implements Serializable { + String s0; + + String s1 = "noear"; + + Boolean b0; + boolean b1 = true; + + Long n0; + Long n1 = 1L; + + Double d0; + Double d1 = 1.0D; + + Object obj0; + List list0; + Map map0; + Map map1; +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/action/ExecutorTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/action/ExecutorTest.java new file mode 100644 index 000000000..8d68f1549 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/action/ExecutorTest.java @@ -0,0 +1,56 @@ +package com.alibaba.fastjson2.support.solon.test.action; + +import org.junit.jupiter.api.Test; +import org.noear.solon.Solon; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Mapping; +import org.noear.solon.annotation.Param; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +/** + * @author noear 2024/9/17 created + */ +@SolonTest +public class ExecutorTest { + @Test + public void test() throws Throwable { + ContextEmpty ctx = new ContextEmpty(); + ctx.headerMap().add("Content-Type", "text/json"); + ctx.pathNew("/a1"); + ctx.bodyNew("{\"name\":\"noear\",\"label\":\"A\"}"); + + Solon.app().tryHandle(ctx); + ctx.result = ctx.attr("output"); + System.out.println(ctx.result); + assert "Hello noear A".equals(ctx.result); + + ctx = new ContextEmpty(); + ctx.headerMap().add("Content-Type", "text/json"); + ctx.pathNew("/a2"); + ctx.bodyNew("{\"name\":\"noear\",\"label\":\"A\"}"); + + Solon.app().tryHandle(ctx); + ctx.result = ctx.attr("output"); + System.out.println(ctx.result); + assert "\"A\"".equals(ctx.result); + } + + @Controller + public static class Demo { + @Mapping("/a1") + public String a1(@Param("name") String name, @Param("label") Label label) { + return "Hello " + name + " " + label; + } + + @Mapping("/a2") + public Label a2(@Param("name") String name, @Param("label") Label label) { + return label; + } + } + + public enum Label { + A, + B + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java new file mode 100644 index 000000000..0acbf332a --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test0/QuickConfigTest.java @@ -0,0 +1,47 @@ +package com.alibaba.fastjson2.support.solon.test.config.test0; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.snack.ONode; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Import(profiles = "classpath:features2_test0.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + assert ONode.load(output).count() == 5; + + assert "{\"b1\":true,\"d1\":1.0,\"map1\":{\"time\":1673861993477,\"long\":12,\"int\":12},\"n1\":1,\"s1\":\"noear\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java new file mode 100644 index 000000000..67c04db8c --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest.java @@ -0,0 +1,48 @@ +package com.alibaba.fastjson2.support.solon.test.config.test1; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.snack.ONode; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Import(profiles = "classpath:features2_test1.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + assert ONode.load(output).count() == 5; + + //完美 + assert "{\"b1\":true,\"d1\":1.0,\"map1\":{\"time\":\"2023-01-16 17:39:53\",\"long\":12,\"int\":12},\"n1\":1,\"s1\":\"noear\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java new file mode 100644 index 000000000..2b878c2b2 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1/QuickConfigTest2.java @@ -0,0 +1,48 @@ +package com.alibaba.fastjson2.support.solon.test.config.test1; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.snack.ONode; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 只对时间进行格式化 + */ +@Import(profiles = "classpath:features2_test1-2.yml") +@SolonTest +public class QuickConfigTest2 { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + assert ONode.load(output).count() == 5; + + //完美 + assert "{\"b1\":true,\"d1\":1.0,\"map1\":{\"time\":1673861993477,\"long\":12,\"int\":12},\"n1\":1,\"s1\":\"noear\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/FormatTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/FormatTest.java new file mode 100644 index 000000000..cd6d9071e --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/FormatTest.java @@ -0,0 +1,60 @@ +package com.alibaba.fastjson2.support.solon.test.config.test1_2; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson2.writer.ObjectWriter; + +import java.lang.reflect.Type; +import java.util.Date; + +/** + * @author noear 2024/9/4 created + */ +public class FormatTest { + public static void main(String[] args) { + // + // 当有 Provider::register 类型处理后,@JSONField 注解失效了 + // + JSONWriter.Context context = new JSONWriter.Context(); + context.getProvider().register(Date.class, new ObjectWriter() { + @Override + public void write(JSONWriter jsonWriter, Object o, Object o1, Type type, long l) { + jsonWriter.writeInt64(((Date) o).getTime()); + } + }); + + CustomDateDo dateDo = new CustomDateDo(); + + dateDo.setDate(new Date(1673861993477L)); + dateDo.setDate2(new Date(1673861993477L)); + + String json = JSON.toJSONString(dateDo, context); + System.out.println(json); //{"date":1673861993477,"date2":1673861993477} + + assert "{\"date\":1673861993477,\"date2\":\"2023-01-16 17:39:53\"}".equals(json); + } + + public static class CustomDateDo { + private Date date; + + @JSONField(format = "yyyy-MM-dd HH:mm:ss") + private Date date2; + + public void setDate(Date date) { + this.date = date; + } + + public void setDate2(Date date2) { + this.date2 = date2; + } + + public Date getDate() { + return date; + } + + public Date getDate2() { + return date2; + } + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/QuickConfigTest.java new file mode 100644 index 000000000..452fed7f7 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test1_2/QuickConfigTest.java @@ -0,0 +1,38 @@ +package com.alibaba.fastjson2.support.solon.test.config.test1_2; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.CustomDateDo; +import org.junit.jupiter.api.Test; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; + +/** + * 时间进行格式化 + long,int 转为字符串 + 常见类型转为非null + 所有null输出 + */ +@Import(profiles = "classpath:features2_test1-2.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + CustomDateDo dateDo = new CustomDateDo(); + + dateDo.setDate(new Date(1673861993477L)); + dateDo.setDate2(new Date(1673861993477L)); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(dateDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + //err: register 类型处理后,JSONField 失效了 + assert "{\"date\":1673861993477,\"date2\":\"2023-01-16 17:39:53\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java new file mode 100644 index 000000000..7c28a3db6 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test2/QuickConfigTest.java @@ -0,0 +1,65 @@ +package com.alibaba.fastjson2.support.solon.test.config.test2; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.OrderDo; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.snack.ONode; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Import(profiles = "classpath:features2_test2.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + assert ONode.load(output).count() == 5; + + //error: int 没转为 string + assert "{\"b1\":1,\"d1\":1.0,\"map1\":{\"time\":\"2023-01-16 17:39:53\",\"long\":\"12\",\"int\":12},\"n1\":\"1\",\"s1\":\"noear\"}".equals(output); + } + + @Test + public void hello3() throws Throwable { + Map data = new LinkedHashMap<>(); + data.put("long", 1L); + data.put("order", new OrderDo()); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(data, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + assert "{\"long\":\"1\",\"order\":{\"orderId\":\"2\"}}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test3/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test3/QuickConfigTest.java new file mode 100644 index 000000000..c2e61a22f --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test3/QuickConfigTest.java @@ -0,0 +1,45 @@ +package com.alibaba.fastjson2.support.solon.test.config.test3; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Import(profiles = "classpath:features2_test3.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + //error: int 没转为 string + assert "{\"b0\":0,\"b1\":1,\"d0\":0,\"d1\":1.0,\"list0\":[],\"map0\":null,\"map1\":{\"null\":null,\"time\":\"2023-01-16 17:39:53\",\"long\":\"12\",\"int\":12},\"n0\":\"0\",\"n1\":\"1\",\"obj0\":null,\"s0\":\"\",\"s1\":\"noear\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test4/QuickConfigTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test4/QuickConfigTest.java new file mode 100644 index 000000000..c291fcab2 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/config/test4/QuickConfigTest.java @@ -0,0 +1,45 @@ +package com.alibaba.fastjson2.support.solon.test.config.test4; + +import com.alibaba.fastjson2.support.solon.Fastjson2RenderFactory; +import com.alibaba.fastjson2.support.solon.test._model.UserDo; +import org.junit.jupiter.api.Test; +import org.noear.solon.annotation.Import; +import org.noear.solon.annotation.Inject; +import org.noear.solon.core.handle.ContextEmpty; +import org.noear.solon.test.SolonTest; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author noear 2023/1/16 created + */ +@Import(profiles = "classpath:features2_test4.yml") +@SolonTest +public class QuickConfigTest { + @Inject + Fastjson2RenderFactory renderFactory; + + @Test + public void hello2() throws Throwable { + UserDo userDo = new UserDo(); + + Map data = new HashMap<>(); + data.put("time", new Date(1673861993477L)); + data.put("long", 12L); + data.put("int", 12); + data.put("null", null); + + userDo.setMap1(data); + + ContextEmpty ctx = new ContextEmpty(); + renderFactory.create().render(userDo, ctx); + String output = ctx.attr("output"); + + System.out.println(output); + + //error: int 没转为 string + assert "{\"b0\":0,\"b1\":1,\"d0\":0,\"d1\":1.0,\"list0\":[],\"map0\":null,\"map1\":{\"null\":null,\"time\":\"2023-01-16 17:39:53\",\"long\":\"12\",\"int\":12},\"n0\":\"0\",\"n1\":\"1\",\"obj0\":null,\"s0\":\"\",\"s1\":\"noear\"}".equals(output); + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest.java new file mode 100644 index 000000000..0661ccaa0 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest.java @@ -0,0 +1,26 @@ +package com.alibaba.fastjson2.support.solon.test.requirement.case1; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author noear 2023/10/29 created + */ +public class AsNumberTest { + @Test + public void test() throws Exception { + String json = JSON.toJSONString(new Bean(), + JSONWriter.Feature.WriteNullNumberAsZero, + JSONWriter.Feature.WriteLongAsString); + + System.out.println(json); + assertEquals("{\"value\":\"0\"}", json); + } + + public static class Bean { + public Long value; + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest2.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest2.java new file mode 100644 index 000000000..7277b6fdc --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest2.java @@ -0,0 +1,41 @@ +package com.alibaba.fastjson2.support.solon.test.requirement.case1; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.writer.ObjectWriterProvider; +import org.junit.jupiter.api.Test; + +/** + * @author noear 2023/10/29 created + */ +public class AsNumberTest2 { + @Test + public void test() throws Exception { + ObjectWriterProvider writerProvider = new ObjectWriterProvider(); + + JSONWriter.Context writeContext = new JSONWriter.Context(writerProvider, + JSONWriter.Feature.WriteNullNumberAsZero); + + Demo demo = new Demo(); + String tmp = JSON.toJSONString(demo, writeContext); + System.out.println(tmp); + + assert "{\"a\":0,\"b\":0,\"c\":1}".equals(tmp); + + writeContext = new JSONWriter.Context(writerProvider, + JSONWriter.Feature.WriteNullNumberAsZero, + JSONWriter.Feature.WriteLongAsString); + + demo = new Demo(); + tmp = JSON.toJSONString(demo, writeContext); + System.out.println(tmp); + + assert "{\"a\":\"0\",\"b\":\"0\",\"c\":\"1\"}".equals(tmp); + } + + public static class Demo { + public long a; + public Long b; + public Long c = 1L; + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest3.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest3.java new file mode 100644 index 000000000..9a4c1c952 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case1/AsNumberTest3.java @@ -0,0 +1,26 @@ +package com.alibaba.fastjson2.support.solon.test.requirement.case1; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author noear 2023/10/29 created + */ +public class AsNumberTest3 { + @Test + public void test() throws Exception { + String json = JSON.toJSONString(new Bean(), + JSONWriter.Feature.WriteNullBooleanAsFalse, + JSONWriter.Feature.WriteBooleanAsNumber); + + System.out.println(json); + assertEquals("{\"value\":0}", json); + } + + public static class Bean { + public Boolean value; + } +} diff --git a/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case2/TypeTest.java b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case2/TypeTest.java new file mode 100644 index 000000000..a2ba91212 --- /dev/null +++ b/extension-solon/src/test/java/com/alibaba/fastjson2/support/solon/test/requirement/case2/TypeTest.java @@ -0,0 +1,25 @@ +package com.alibaba.fastjson2.support.solon.test.requirement.case2; + +/** + * @author noear 2023/10/29 created + */ +public class TypeTest { +// @Test +// public void test() throws Throwable { +// Bean data = new Bean(); +// data.value = 12L; +// +// String output = JSON.toJSONString(data, JSONWriter.Feature.WriteClassName); +// +// System.out.println(output); //{"@type":"features.type0.TypeTest$Bean","value":12L} +// assertEquals("{\"@type\":\"features.type0.TypeTest$Bean\",\"value\":12}", output); +// } + + public static class Bean { + private Long value; + + public Long getValue() { + return value; + } + } +} diff --git a/extension-solon/src/test/resources/features2_test0.yml b/extension-solon/src/test/resources/features2_test0.yml new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/extension-solon/src/test/resources/features2_test0.yml @@ -0,0 +1 @@ + diff --git a/extension-solon/src/test/resources/features2_test1-2.yml b/extension-solon/src/test/resources/features2_test1-2.yml new file mode 100644 index 000000000..00e4125fe --- /dev/null +++ b/extension-solon/src/test/resources/features2_test1-2.yml @@ -0,0 +1,4 @@ + + +solon.serialization.json: + dateAsTicks: true #将date转为毫秒数 \ No newline at end of file diff --git a/extension-solon/src/test/resources/features2_test1.yml b/extension-solon/src/test/resources/features2_test1.yml new file mode 100644 index 000000000..7022f983a --- /dev/null +++ b/extension-solon/src/test/resources/features2_test1.yml @@ -0,0 +1,5 @@ + + +solon.serialization.json: + dateAsFormat: 'yyyy-MM-dd HH:mm:ss' #配置日期格式(默认输出为时间戳) + dateAsTimeZone: 'GMT+8' #配置时区 \ No newline at end of file diff --git a/extension-solon/src/test/resources/features2_test2.yml b/extension-solon/src/test/resources/features2_test2.yml new file mode 100644 index 000000000..525c66b49 --- /dev/null +++ b/extension-solon/src/test/resources/features2_test2.yml @@ -0,0 +1,13 @@ + + +solon.serialization.json: + dateAsFormat: 'yyyy-MM-dd HH:mm:ss' #配置日期格式(默认输出为时间戳) + dateAsTimeZone: 'GMT+8' #配置时区 + longAsString: true #将long型转为字符串输出 (默认为false) + intAsString: true #将int型转为字符串输出 (默认为false) + boolAsInt: true #将bool型转为字符串输出 (默认为false) + nullStringAsEmpty: false + nullBoolAsFalse: false + nullNumberAsZero: false + nullArrayAsEmpty: false + nullAsWriteable: false \ No newline at end of file diff --git a/extension-solon/src/test/resources/features2_test3.yml b/extension-solon/src/test/resources/features2_test3.yml new file mode 100644 index 000000000..3ee4a8e62 --- /dev/null +++ b/extension-solon/src/test/resources/features2_test3.yml @@ -0,0 +1,13 @@ + + +solon.serialization.json: + dateAsFormat: 'yyyy-MM-dd HH:mm:ss' #配置日期格式(默认输出为时间戳) + dateAsTimeZone: 'GMT+8' #配置时区 + longAsString: true #将long型转为字符串输出 (默认为false) + intAsString: true #将int型转为字符串输出 (默认为false) + boolAsInt: true #将bool型转为字符串输出 (默认为false) + nullStringAsEmpty: true + nullBoolAsFalse: true + nullNumberAsZero: true + nullArrayAsEmpty: true + nullAsWriteable: false \ No newline at end of file diff --git a/extension-solon/src/test/resources/features2_test4.yml b/extension-solon/src/test/resources/features2_test4.yml new file mode 100644 index 000000000..7e0123907 --- /dev/null +++ b/extension-solon/src/test/resources/features2_test4.yml @@ -0,0 +1,13 @@ + + +solon.serialization.json: + dateAsFormat: 'yyyy-MM-dd HH:mm:ss' #配置日期格式(默认输出为时间戳) + dateAsTimeZone: 'GMT+8' #配置时区 + longAsString: true #将long型转为字符串输出 (默认为false) + intAsString: true #将int型转为字符串输出 (默认为false) + boolAsInt: true #将bool型转为字符串输出 (默认为false) + nullStringAsEmpty: true + nullBoolAsFalse: true + nullNumberAsZero: true + nullArrayAsEmpty: true + nullAsWriteable: true \ No newline at end of file diff --git a/pom.xml b/pom.xml index 31930839c..56680fb36 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 2.17.2 11.0.15 2.44 + 3.0.1 5.3.31 6.1.3 5.8.8 @@ -71,11 +72,13 @@ + example-solon-test example-spring-test extension + extension-solon extension-spring5