From b84006d8db49aa79d66a3b5f12ebdf07c89242b8 Mon Sep 17 00:00:00 2001 From: redkale Date: Sat, 29 Jul 2023 09:20:13 +0800 Subject: [PATCH] =?UTF-8?q?ConvertFactory=E5=A2=9E=E5=8A=A0nullable?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/redkale/boot/ApiDocCommand.java | 211 +++++++++++++----- .../org/redkale/convert/ConvertCoder.java | 27 ++- .../org/redkale/convert/ConvertFactory.java | 38 +++- .../org/redkale/convert/ConvertProvider.java | 2 +- .../redkale/convert/ConvertSmallString.java | 4 +- .../org/redkale/convert/ObjectDecoder.java | 6 +- .../org/redkale/convert/ObjectEncoder.java | 6 +- src/main/java/org/redkale/convert/Writer.java | 18 ++ .../convert/bson/BsonByteBufferWriter.java | 11 +- .../org/redkale/convert/bson/BsonConvert.java | 17 +- .../org/redkale/convert/bson/BsonFactory.java | 29 ++- .../convert/bson/BsonStreamWriter.java | 4 +- .../org/redkale/convert/bson/BsonWriter.java | 12 + .../convert/json/JsonByteBufferWriter.java | 24 +- .../redkale/convert/json/JsonBytesWriter.java | 26 ++- .../redkale/convert/json/JsonCharsWriter.java | 17 ++ .../org/redkale/convert/json/JsonConvert.java | 19 +- .../redkale/convert/json/JsonDynEncoder.java | 8 +- .../org/redkale/convert/json/JsonFactory.java | 29 ++- .../convert/json/JsonStreamWriter.java | 12 +- .../org/redkale/convert/json/JsonWriter.java | 12 + .../redkale/test/convert/BsonMainTest.java | 17 +- .../org/redkale/test/convert/Json5Test.java | 14 +- .../redkale/test/convert/JsonMainTest.java | 3 +- 24 files changed, 436 insertions(+), 130 deletions(-) diff --git a/src/main/java/org/redkale/boot/ApiDocCommand.java b/src/main/java/org/redkale/boot/ApiDocCommand.java index 617c40b89..1d3ec1851 100644 --- a/src/main/java/org/redkale/boot/ApiDocCommand.java +++ b/src/main/java/org/redkale/boot/ApiDocCommand.java @@ -60,7 +60,9 @@ public final class ApiDocCommand { if (params != null && params.length > 0) { for (String param : params) { - if (param == null) continue; + if (param == null) { + continue; + } param = param.toLowerCase(); if (param.startsWith("--api-skiprpc=")) { skipRPC = "true".equalsIgnoreCase(param.substring("--api-skiprpc=".length())); @@ -80,7 +82,9 @@ public final class ApiDocCommand { List swaggerTags = new ArrayList<>(); Map> swaggerComponentsMap = new LinkedHashMap<>(); for (NodeServer node : app.servers) { - if (!(node instanceof NodeHttpServer)) continue; + if (!(node instanceof NodeHttpServer)) { + continue; + } final Map map = new LinkedHashMap<>(); serverList.add(map); HttpServer server = node.getServer(); @@ -89,12 +93,20 @@ public final class ApiDocCommand { List> servletsList = new ArrayList<>(); map.put("servlets", servletsList); String plainContentType = server.getResponseConfig() == null ? "application/json" : server.getResponseConfig().plainContentType; - if (plainContentType == null || plainContentType.isEmpty()) plainContentType = "application/json"; - if (plainContentType.indexOf(';') > 0) plainContentType = plainContentType.substring(0, plainContentType.indexOf(';')); + if (plainContentType == null || plainContentType.isEmpty()) { + plainContentType = "application/json"; + } + if (plainContentType.indexOf(';') > 0) { + plainContentType = plainContentType.substring(0, plainContentType.indexOf(';')); + } for (HttpServlet servlet : server.getDispatcherServlet().getServlets()) { - if (!(servlet instanceof HttpServlet)) continue; - if (servlet instanceof WebSocketServlet) continue; + if (!(servlet instanceof HttpServlet)) { + continue; + } + if (servlet instanceof WebSocketServlet) { + continue; + } if (servlet.getClass().getAnnotation(MessageMultiConsumer.class) != null) { node.logger.log(Level.INFO, servlet + " be skipped because has @MessageMultiConsumer"); continue; @@ -128,16 +140,29 @@ public final class ApiDocCommand { Class clz = servlet.getClass(); HashSet actionUrls = new HashSet<>(); do { - if (Modifier.isAbstract(clz.getModifiers())) break; + if (Modifier.isAbstract(clz.getModifiers())) { + break; + } for (Method method : clz.getMethods()) { - if (method.getParameterCount() != 2) continue; + if (method.getParameterCount() != 2) { + continue; + } HttpMapping action = method.getAnnotation(HttpMapping.class); - if (action == null) continue; - if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法 - if (actionUrls.contains(action.url())) continue; - if (HttpScope.class.isAssignableFrom(action.result())) continue; //忽略模板引擎的方法 - if (action.rpconly() && skipRPC) continue; //不生成RPC接口 - + if (action == null) { + continue; + } + if (!action.inherited() && selfClz != clz) { + continue; //忽略不被继承的方法 + } + if (actionUrls.contains(action.url())) { + continue; + } + if (HttpScope.class.isAssignableFrom(action.result())) { + continue; //忽略模板引擎的方法 + } + if (action.rpconly() && skipRPC) { + continue; //不生成RPC接口 + } final List> swaggerParamsList = new ArrayList<>(); final Map mappingMap = new LinkedHashMap<>(); @@ -241,20 +266,36 @@ public final class ApiDocCommand { swaggerParamsList.add(swaggerParamMap); } } - if (param.style() == HttpParam.HttpParameterStyle.BODY) hasbodyparam = true; - if (ptype.isPrimitive() || ptype == String.class) continue; - if (typesMap.containsKey(ptype.getName())) continue; - if (ptype.getName().startsWith("java.")) continue; - if (ptype.getName().startsWith("javax.")) continue; + if (param.style() == HttpParam.HttpParameterStyle.BODY) { + hasbodyparam = true; + } + if (ptype.isPrimitive() || ptype == String.class) { + continue; + } + if (typesMap.containsKey(ptype.getName())) { + continue; + } + if (ptype.getName().startsWith("java.")) { + continue; + } + if (ptype.getName().startsWith("javax.")) { + continue; + } final Map> typeMap = new LinkedHashMap<>(); Class loop = ptype; final boolean filter = FilterBean.class.isAssignableFrom(loop); do { - if (loop == null || loop.isInterface()) break; + if (loop == null || loop.isInterface()) { + break; + } for (Field field : loop.getDeclaredFields()) { - if (Modifier.isFinal(field.getModifiers())) continue; - if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) { + continue; + } + if (Modifier.isStatic(field.getModifiers())) { + continue; + } Map fieldmap = new LinkedHashMap<>(); fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName()); @@ -276,7 +317,9 @@ public final class ApiDocCommand { fieldmap.put("updatable", (filter || col == null || col.updatable())); if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) { - if (field.getAnnotation(RestAddress.class) != null) continue; + if (field.getAnnotation(RestAddress.class) != null) { + continue; + } } typeMap.put(field.getName(), fieldmap); @@ -301,11 +344,17 @@ public final class ApiDocCommand { Map respMap = new LinkedHashMap<>(); respMap.put("schema", respSchemaMap); Object example = formatExample(returnFactory, action.example(), action.result(), resultType); - if (example != null) respSchemaMap.put("example", example); - if (!swaggerRequestBody.isEmpty()) swaggerOperatMap.put("requestBody", swaggerRequestBody); + if (example != null) { + respSchemaMap.put("example", example); + } + if (!swaggerRequestBody.isEmpty()) { + swaggerOperatMap.put("requestBody", swaggerRequestBody); + } swaggerOperatMap.put("parameters", swaggerParamsList); String actiondesc = action.comment(); - if (action.rpconly()) actiondesc = "[Only for RPC API] " + actiondesc; + if (action.rpconly()) { + actiondesc = "[Only for RPC API] " + actiondesc; + } swaggerOperatMap.put("responses", Utility.ofMap("200", Utility.ofMap("description", actiondesc, "content", Utility.ofMap("application/json", respMap)))); String m = action.methods() == null || action.methods().length == 0 ? null : action.methods()[0].toLowerCase(); @@ -319,7 +368,9 @@ public final class ApiDocCommand { } while ((clz = clz.getSuperclass()) != HttpServlet.class); mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url"))); servletsList.add(servletMap); - if (!actionUrls.isEmpty()) swaggerTags.add(Utility.ofMap("name", tag, "description", ws.comment())); + if (!actionUrls.isEmpty()) { + swaggerTags.add(Utility.ofMap("name", tag, "description", ws.comment())); + } } servletsList.sort((o1, o2) -> { String[] urlregs1 = (String[]) o1.get("urlregs"); @@ -337,7 +388,9 @@ public final class ApiDocCommand { swaggerResultMap.put("servers", swaggerServers); swaggerResultMap.put("paths", swaggerPathsMap); swaggerResultMap.put("tags", swaggerTags); - if (!swaggerComponentsMap.isEmpty()) swaggerResultMap.put("components", Utility.ofMap("schemas", swaggerComponentsMap)); + if (!swaggerComponentsMap.isEmpty()) { + swaggerResultMap.put("components", Utility.ofMap("schemas", swaggerComponentsMap)); + } final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "openapi-doc.json")); out.write(JsonConvert.root().convertTo(swaggerResultMap).getBytes(StandardCharsets.UTF_8)); out.close(); @@ -421,8 +474,12 @@ public final class ApiDocCommand { Set types = new HashSet<>(); Encodeable encodeable = JsonFactory.root().loadEncoder(genericType); String ct = componentKey(factory, logger, types, componentsMap, null, encodeable, true); - if (ct == null || ct.length() == 0) return null; - if (componentsMap.containsKey(ct)) return ct; + if (ct == null || ct.length() == 0) { + return null; + } + if (componentsMap.containsKey(ct)) { + return ct; + } Map cmap = new LinkedHashMap<>(); componentsMap.put(ct, cmap); //必须在调用simpleSchemaType之前put,不然嵌套情况下死循环 @@ -440,11 +497,15 @@ public final class ApiDocCommand { FilterColumn fcol = member.getField().getAnnotation(FilterColumn.class); if (fcol != null) { desc = fcol.comment(); - if (fcol.required()) requireds.add(member.getAttribute().field()); + if (fcol.required()) { + requireds.add(member.getAttribute().field()); + } } } else { desc = col.comment(); - if (!col.nullable()) requireds.add(member.getAttribute().field()); + if (!col.nullable()) { + requireds.add(member.getAttribute().field()); + } } if (desc.isEmpty() && member.getField().getAnnotation(Comment.class) != null) { desc = member.getField().getAnnotation(Comment.class).value(); @@ -457,11 +518,15 @@ public final class ApiDocCommand { FilterColumn fcol = member.getMethod().getAnnotation(FilterColumn.class); if (fcol != null) { desc = fcol.comment(); - if (fcol.required()) requireds.add(member.getAttribute().field()); + if (fcol.required()) { + requireds.add(member.getAttribute().field()); + } } } else { desc = col.comment(); - if (!col.nullable()) requireds.add(member.getAttribute().field()); + if (!col.nullable()) { + requireds.add(member.getAttribute().field()); + } } if (desc.isEmpty() && member.getMethod().getAnnotation(Comment.class) != null) { desc = member.getMethod().getAnnotation(Comment.class).value(); @@ -469,11 +534,15 @@ public final class ApiDocCommand { desc = member.getMethod().getAnnotation(org.redkale.util.Comment.class).value(); } } - if (!desc.isEmpty()) schemaMap.put("description", desc); + if (!desc.isEmpty()) { + schemaMap.put("description", desc); + } properties.put(member.getAttribute().field(), schemaMap); } } - if (!requireds.isEmpty()) cmap.put("required", requireds); + if (!requireds.isEmpty()) { + cmap.put("required", requireds); + } cmap.put("properties", properties); return ct; } catch (Exception e) { @@ -484,7 +553,9 @@ public final class ApiDocCommand { private static String componentKey(JsonFactory factory, Logger logger, Set types, Map> componentsMap, EnMember field, Encodeable encodeable, boolean first) { if (encodeable instanceof ObjectEncoder) { - if (types.contains(encodeable.getType())) return ""; + if (types.contains(encodeable.getType())) { + return ""; + } types.add(encodeable.getType()); StringBuilder sb = new StringBuilder(); sb.append(((ObjectEncoder) encodeable).getTypeClass().getSimpleName()); @@ -492,19 +563,33 @@ public final class ApiDocCommand { if (member.getEncoder() instanceof ArrayEncoder || member.getEncoder() instanceof CollectionEncoder) { String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false); - if (subsb == null) return null; + if (subsb == null) { + return null; + } AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField(); - if (real == null) continue; + if (real == null) { + continue; + } Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType(); Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType(); - if (cz == ct) continue; - if (field == null && encodeable.getType() instanceof Class) continue; - if (sb.length() > 0 && subsb.length() > 0) sb.append("_"); + if (cz == ct) { + continue; + } + if (field == null && encodeable.getType() instanceof Class) { + continue; + } + if (sb.length() > 0 && subsb.length() > 0) { + sb.append("_"); + } sb.append(subsb); } else if (member.getEncoder() instanceof ObjectEncoder || member.getEncoder() instanceof SimpledCoder) { AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField(); - if (real == null) continue; - if (types.contains(member.getEncoder().getType())) continue; + if (real == null) { + continue; + } + if (types.contains(member.getEncoder().getType())) { + continue; + } types.add(member.getEncoder().getType()); if (member.getEncoder() instanceof SimpledCoder) { simpleSchemaType(factory, logger, componentsMap, ((SimpledCoder) member.getEncoder()).getType(), ((SimpledCoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true); @@ -513,11 +598,19 @@ public final class ApiDocCommand { } Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType(); Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType(); - if (cz == ct) continue; + if (cz == ct) { + continue; + } String subsb = componentKey(factory, logger, types, componentsMap, member, member.getEncoder(), false); - if (subsb == null) return null; - if (field == null && member.getEncoder().getType() instanceof Class) continue; - if (sb.length() > 0 && subsb.length() > 0) sb.append("_"); + if (subsb == null) { + return null; + } + if (field == null && member.getEncoder().getType() instanceof Class) { + continue; + } + if (sb.length() > 0 && subsb.length() > 0) { + sb.append("_"); + } sb.append(subsb); } else if (member.getEncoder() instanceof MapEncoder) { continue; @@ -529,9 +622,13 @@ public final class ApiDocCommand { } else if (encodeable instanceof ArrayEncoder || encodeable instanceof CollectionEncoder) { final boolean array = (encodeable instanceof ArrayEncoder); Encodeable subEncodeable = array ? ((ArrayEncoder) encodeable).getComponentEncoder() : ((CollectionEncoder) encodeable).getComponentEncoder(); - if (subEncodeable instanceof SimpledCoder && field != null) return ""; + if (subEncodeable instanceof SimpledCoder && field != null) { + return ""; + } final String sb = componentKey(factory, logger, types, componentsMap, null, subEncodeable, false); - if (sb == null || sb.isEmpty()) return sb; + if (sb == null || sb.isEmpty()) { + return sb; + } if (field != null && field.getField() != null && field.getField().getDeclaringClass() == Sheet.class) { return sb; } @@ -550,7 +647,9 @@ public final class ApiDocCommand { } private static Object formatExample(JsonFactory factory, String example, Class type, Type genericType) { - if (example != null && !example.isEmpty()) return example; + if (example != null && !example.isEmpty()) { + return example; + } JsonFactory jsonFactory = factory == null || factory == JsonFactory.root() ? exampleFactory : factory; if (type == Flipper.class) { return new Flipper(); @@ -642,8 +741,12 @@ public final class ApiDocCommand { json.append("{"); int index = 0; for (DeMember member : ((ObjectDecoder) decoder).getMembers()) { - if (!(member.getDecoder() instanceof ObjectDecoder)) continue; - if (index > 0) json.append(","); + if (!(member.getDecoder() instanceof ObjectDecoder)) { + continue; + } + if (index > 0) { + json.append(","); + } json.append('"').append(member.getAttribute().field()).append("\":{}"); index++; } @@ -659,6 +762,6 @@ public final class ApiDocCommand { return example; } - private static final JsonFactory exampleFactory = JsonFactory.create().tiny(false); + private static final JsonFactory exampleFactory = JsonFactory.create().tiny(false).nullable(false); } diff --git a/src/main/java/org/redkale/convert/ConvertCoder.java b/src/main/java/org/redkale/convert/ConvertCoder.java index 5e50694c4..a1d80bfb8 100644 --- a/src/main/java/org/redkale/convert/ConvertCoder.java +++ b/src/main/java/org/redkale/convert/ConvertCoder.java @@ -24,21 +24,42 @@ import static java.lang.annotation.RetentionPolicy.*; public @interface ConvertCoder { /** - * 需要指定的字段类型,指定了coder字段值则可以不设置此字段 + * 需要指定的字段类型,类型必须是原字段类型的子类。 + * 例如:
+ *
+     * @ConvertCoder(column = String.class)
+     * private CharSequence name;
+     * 
+ * + * 通常此字段值与encoder/decoder是二选一,指定了coder字段值则可以不设置此字段。 * * @return 字段类名 */ Class column() default Object.class; /** - * 序列化定制化的 Encodeable + * 序列化定制化的 Encodeable, 构造函数的参数可以是:
+ * 1、ConvertFactory + * 2、Type + * 3、Class + * 4、ConvertFactory和Type + * 5、ConvertFactory和Class + * + * 类如果存在instance单实例对象字段值,则优先使用instance对象 * * @return Encodeable 类 */ Class encoder() default Encodeable.class; /** - * 反序列化定制化的 Decodeable + * 反序列化定制化的 Decodeable, 构造函数的参数可以是:
+ * 1、ConvertFactory + * 2、Type + * 3、Class + * 4、ConvertFactory和Type + * 5、ConvertFactory和Class + * + * 类如果存在instance单实例对象字段值,则优先使用instance对象 * * @return Decodeable 类 */ diff --git a/src/main/java/org/redkale/convert/ConvertFactory.java b/src/main/java/org/redkale/convert/ConvertFactory.java index 880320f24..8425771d0 100644 --- a/src/main/java/org/redkale/convert/ConvertFactory.java +++ b/src/main/java/org/redkale/convert/ConvertFactory.java @@ -44,7 +44,9 @@ public abstract class ConvertFactory { protected Convert convert; - protected boolean tiny; //String类型值为"",Boolean类型值为false时是否需要输出, 默认为false + protected boolean tiny; //值为true时 String类型值为"",Boolean类型值为false时不会输出,默认为false + + protected boolean nullable; ///值为true时 字段值为null时会输出,默认为false private final Encodeable anyEncoder = new AnyEncoder(this); @@ -72,8 +74,9 @@ public abstract class ConvertFactory { private boolean skipAllIgnore = false; - protected ConvertFactory(ConvertFactory parent, boolean tiny) { + protected ConvertFactory(ConvertFactory parent, boolean tiny, boolean nullable) { this.tiny = tiny; + this.nullable = nullable; this.parent = parent; if (parent == null) { //--------------------------------------------------------- @@ -251,7 +254,7 @@ public abstract class ConvertFactory { public abstract ConvertFactory createChild(); - public abstract ConvertFactory createChild(boolean tiny); + public abstract ConvertFactory createChild(boolean tiny, boolean nullable); protected SimpledCoder createEnumSimpledCoder(Class enumClass) { return new EnumSimpledCoder(this, enumClass); @@ -335,6 +338,11 @@ public abstract class ConvertFactory { return this; } + public ConvertFactory nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + public boolean isConvertDisabled(AccessibleObject element) { ConvertDisabled[] ccs = element.getAnnotationsByType(ConvertDisabled.class); if (ccs.length == 0 && element instanceof Method) { @@ -630,7 +638,7 @@ public abstract class ConvertFactory { } } - ConvertFactory columnFactory(Class type, ConvertCoder[] coders, boolean encode) { + ConvertFactory columnFactory(Type type, ConvertCoder[] coders, boolean encode) { if (coders == null || coders.length < 1) { return this; } @@ -642,7 +650,7 @@ public abstract class ConvertFactory { if (!ann.type().contains(ct)) { continue; } - Class colType = type; + Type colType = type; if (ann.column() != Object.class) { colType = ann.column(); } @@ -679,7 +687,9 @@ public abstract class ConvertFactory { if (paramTypes.length == 0) { encoder = creator.create(); } else if (paramTypes.length == 1) { - if (Type.class.isAssignableFrom(paramTypes[0])) { + if (Class.class.isAssignableFrom(paramTypes[0])) { + encoder = creator.create((Object) TypeToken.typeToClass(colType)); + } else if (Type.class.isAssignableFrom(paramTypes[0])) { encoder = creator.create((Object) colType); } else if (ConvertFactory.class.isAssignableFrom(paramTypes[0])) { encoder = creator.create(this); @@ -687,7 +697,11 @@ public abstract class ConvertFactory { throw new ConvertException(enClazz + " not found public empty-parameter Constructor"); } } else if (paramTypes.length == 2) { - if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Type.class.isAssignableFrom(paramTypes[1])) { + if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Class.class.isAssignableFrom(paramTypes[1])) { + encoder = creator.create(this, TypeToken.typeToClass(colType)); + } else if (Class.class.isAssignableFrom(paramTypes[0]) && ConvertFactory.class.isAssignableFrom(paramTypes[1])) { + encoder = creator.create(TypeToken.typeToClass(colType), this); + } else if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Type.class.isAssignableFrom(paramTypes[1])) { encoder = creator.create(this, colType); } else if (Type.class.isAssignableFrom(paramTypes[0]) && ConvertFactory.class.isAssignableFrom(paramTypes[1])) { encoder = creator.create(colType, this); @@ -740,7 +754,9 @@ public abstract class ConvertFactory { if (paramTypes.length == 0) { decoder = creator.create(); } else if (paramTypes.length == 1) { - if (Type.class.isAssignableFrom(paramTypes[0])) { + if (Class.class.isAssignableFrom(paramTypes[0])) { + decoder = creator.create((Object) TypeToken.typeToClass(colType)); + } else if (Type.class.isAssignableFrom(paramTypes[0])) { decoder = creator.create((Object) colType); } else if (ConvertFactory.class.isAssignableFrom(paramTypes[0])) { decoder = creator.create(this); @@ -748,7 +764,11 @@ public abstract class ConvertFactory { throw new ConvertException(deClazz + " not found public empty-parameter Constructor"); } } else if (paramTypes.length == 2) { - if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Type.class.isAssignableFrom(paramTypes[1])) { + if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Class.class.isAssignableFrom(paramTypes[1])) { + decoder = creator.create(this, TypeToken.typeToClass(colType)); + } else if (Class.class.isAssignableFrom(paramTypes[0]) && ConvertFactory.class.isAssignableFrom(paramTypes[1])) { + decoder = creator.create(TypeToken.typeToClass(colType), this); + } else if (ConvertFactory.class.isAssignableFrom(paramTypes[0]) && Type.class.isAssignableFrom(paramTypes[1])) { decoder = creator.create(this, colType); } else if (Type.class.isAssignableFrom(paramTypes[0]) && ConvertFactory.class.isAssignableFrom(paramTypes[1])) { decoder = creator.create(colType, this); diff --git a/src/main/java/org/redkale/convert/ConvertProvider.java b/src/main/java/org/redkale/convert/ConvertProvider.java index a4521052b..0599407cb 100644 --- a/src/main/java/org/redkale/convert/ConvertProvider.java +++ b/src/main/java/org/redkale/convert/ConvertProvider.java @@ -6,7 +6,7 @@ package org.redkale.convert; /** - * Convert的扩展实现类加载器 + * Convert的扩展实现类加载器, 通过此类可以创建自定义的序列化格式,例如:protobuf、xmlbean * * * 详情见: https://redkale.org diff --git a/src/main/java/org/redkale/convert/ConvertSmallString.java b/src/main/java/org/redkale/convert/ConvertSmallString.java index 4fcdd637f..6494b608f 100644 --- a/src/main/java/org/redkale/convert/ConvertSmallString.java +++ b/src/main/java/org/redkale/convert/ConvertSmallString.java @@ -5,12 +5,12 @@ */ package org.redkale.convert; +import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串 + * 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串,通常用于类名、字段名、枚举值字符串等 * *

* 详情见: https://redkale.org diff --git a/src/main/java/org/redkale/convert/ObjectDecoder.java b/src/main/java/org/redkale/convert/ObjectDecoder.java index 2498e991f..7d03d95de 100644 --- a/src/main/java/org/redkale/convert/ObjectDecoder.java +++ b/src/main/java/org/redkale/convert/ObjectDecoder.java @@ -114,7 +114,7 @@ public class ObjectDecoder implements Decodeable { continue; } ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); - colFactory = factory.columnFactory(field.getType(), field.getAnnotationsByType(ConvertCoder.class), false); + colFactory = factory.columnFactory(field.getGenericType(), field.getAnnotationsByType(ConvertCoder.class), false); Decodeable fieldCoder; if (small != null && field.getType() == String.class) { fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; @@ -197,9 +197,9 @@ public class ObjectDecoder implements Decodeable { ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); Field maybeField = ConvertFactory.readGetSetField(method); - colFactory = factory.columnFactory(method.getParameterTypes()[0], method.getAnnotationsByType(ConvertCoder.class), false); + colFactory = factory.columnFactory(method.getGenericParameterTypes()[0], method.getAnnotationsByType(ConvertCoder.class), false); if (maybeField != null && colFactory == factory) { - colFactory = factory.columnFactory(maybeField.getType(), maybeField.getAnnotationsByType(ConvertCoder.class), false); + colFactory = factory.columnFactory(maybeField.getGenericType(), maybeField.getAnnotationsByType(ConvertCoder.class), false); } Decodeable fieldCoder; if (small != null && method.getParameterTypes()[0] == String.class) { diff --git a/src/main/java/org/redkale/convert/ObjectEncoder.java b/src/main/java/org/redkale/convert/ObjectEncoder.java index ecefa0076..8d1cd0b2e 100644 --- a/src/main/java/org/redkale/convert/ObjectEncoder.java +++ b/src/main/java/org/redkale/convert/ObjectEncoder.java @@ -95,7 +95,7 @@ public class ObjectEncoder implements Encodeable { continue; } ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); - colFactory = factory.columnFactory(field.getType(), field.getAnnotationsByType(ConvertCoder.class), true); + colFactory = factory.columnFactory(field.getGenericType(), field.getAnnotationsByType(ConvertCoder.class), true); Encodeable fieldCoder; if (small != null && field.getType() == String.class) { fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; @@ -165,9 +165,9 @@ public class ObjectEncoder implements Encodeable { } } Field maybeField = ConvertFactory.readGetSetField(method); - colFactory = factory.columnFactory(method.getReturnType(), method.getAnnotationsByType(ConvertCoder.class), true); + colFactory = factory.columnFactory(method.getGenericReturnType(), method.getAnnotationsByType(ConvertCoder.class), true); if (maybeField != null && colFactory == factory) { - colFactory = factory.columnFactory(maybeField.getType(), maybeField.getAnnotationsByType(ConvertCoder.class), true); + colFactory = factory.columnFactory(maybeField.getGenericType(), maybeField.getAnnotationsByType(ConvertCoder.class), true); } Encodeable fieldCoder; if (small != null && method.getReturnType() == String.class) { diff --git a/src/main/java/org/redkale/convert/Writer.java b/src/main/java/org/redkale/convert/Writer.java index 2378c1408..c2977a3bb 100644 --- a/src/main/java/org/redkale/convert/Writer.java +++ b/src/main/java/org/redkale/convert/Writer.java @@ -74,6 +74,13 @@ public abstract class Writer { */ public abstract boolean tiny(); + /** + * 当nullable=true时, 字段值为null时会输出该字段 + * + * @return 是否简化 + */ + public abstract boolean nullable(); + /** * 输出null值 */ @@ -132,6 +139,12 @@ public abstract class Writer { value = objFieldFunc.apply(member.attribute, obj); } if (value == null) { + if (nullable()) { + Attribute attr = member.getAttribute(); + this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition()); + writeNull(); + this.comma = true; + } return; } if (tiny()) { @@ -164,6 +177,11 @@ public abstract class Writer { @SuppressWarnings("unchecked") public void writeObjectField(final String fieldName, Type fieldType, int fieldPos, Encodeable anyEncoder, Object value) { if (value == null) { + if (nullable()) { + this.writeFieldName(null, fieldName, fieldType, fieldPos); + writeNull(); + this.comma = true; + } return; } if (fieldType == null) { diff --git a/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java b/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java index eeb9e718b..1253ffac4 100644 --- a/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java @@ -26,12 +26,13 @@ public class BsonByteBufferWriter extends BsonWriter { private int index; public BsonByteBufferWriter(Supplier supplier) { - this(false, supplier); + this(false, false, supplier); } - protected BsonByteBufferWriter(boolean tiny, Supplier supplier) { + protected BsonByteBufferWriter(boolean tiny, boolean nullable, Supplier supplier) { super((byte[]) null); this.tiny = tiny; + this.nullable = nullable; this.supplier = supplier; } @@ -76,6 +77,12 @@ public class BsonByteBufferWriter extends BsonWriter { return this; } + @Override + public BsonByteBufferWriter nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + @Override protected int expand(final int byteLength) { if (this.buffers == null) { diff --git a/src/main/java/org/redkale/convert/bson/BsonConvert.java b/src/main/java/org/redkale/convert/bson/BsonConvert.java index f10d254a7..58796317f 100644 --- a/src/main/java/org/redkale/convert/bson/BsonConvert.java +++ b/src/main/java/org/redkale/convert/bson/BsonConvert.java @@ -48,9 +48,12 @@ public class BsonConvert extends BinaryConvert { private final boolean tiny; - protected BsonConvert(ConvertFactory factory, boolean tiny) { + private final boolean nullable; + + protected BsonConvert(ConvertFactory factory, boolean tiny, boolean nullable) { super(factory); this.tiny = tiny; + this.nullable = nullable; } @Override @@ -79,7 +82,7 @@ public class BsonConvert extends BinaryConvert { @Override public BsonConvert newConvert(final BiFunction fieldFunc, BiFunction mapFieldFunc, Function objExtFunc) { - return new BsonConvert(getFactory(), tiny) { + return new BsonConvert(getFactory(), tiny, nullable) { @Override protected S configWrite(S writer) { return fieldFunc(writer, fieldFunc, mapFieldFunc, objExtFunc); @@ -117,11 +120,11 @@ public class BsonConvert extends BinaryConvert { //------------------------------ writer ----------------------------------------------------------- public BsonByteBufferWriter pollWriter(final Supplier supplier) { - return configWrite(new BsonByteBufferWriter(tiny, supplier)); + return configWrite(new BsonByteBufferWriter(tiny, nullable, supplier)); } protected BsonWriter pollWriter(final OutputStream out) { - return configWrite(new BsonStreamWriter(tiny, out)); + return configWrite(new BsonStreamWriter(tiny, nullable, out)); } @Override @@ -132,7 +135,7 @@ public class BsonConvert extends BinaryConvert { } else { writerPool.set(null); } - return configWrite(writer.tiny(tiny)); + return configWrite(writer.tiny(tiny).nullable(nullable)); } @Override @@ -237,7 +240,7 @@ public class BsonConvert extends BinaryConvert { @Override public void convertToBytes(final ByteArray array, final Type type, final Object value) { Objects.requireNonNull(array); - final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny)); + final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny).nullable(nullable)); if (value == null) { writer.writeNull(); } else { @@ -283,7 +286,7 @@ public class BsonConvert extends BinaryConvert { if (value == null) { return null; } - final BsonWriter writer = writerPool.get().tiny(tiny); + final BsonWriter writer = writerPool.get().tiny(tiny).nullable(nullable); factory.loadEncoder(type == null ? value.getClass() : type).convertTo(writer, value); return writer; } diff --git a/src/main/java/org/redkale/convert/bson/BsonFactory.java b/src/main/java/org/redkale/convert/bson/BsonFactory.java index a5809e603..1c23d427a 100644 --- a/src/main/java/org/redkale/convert/bson/BsonFactory.java +++ b/src/main/java/org/redkale/convert/bson/BsonFactory.java @@ -24,7 +24,9 @@ import org.redkale.util.TypeToken; @SuppressWarnings("unchecked") public final class BsonFactory extends ConvertFactory { - private static final BsonFactory instance = new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", false)); + private static final BsonFactory instance = new BsonFactory(null, + getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true), + getSystemPropertyBoolean("redkale.convert.bson.nullable", "redkale.convert.nullable", false)); static final Decodeable objectDecoder = instance.loadDecoder(Object.class); @@ -48,8 +50,8 @@ public final class BsonFactory extends ConvertFactory { //instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); } - private BsonFactory(BsonFactory parent, boolean tiny) { - super(parent, tiny); + private BsonFactory(BsonFactory parent, boolean tiny, boolean nullable) { + super(parent, tiny, nullable); } @Override @@ -62,6 +64,16 @@ public final class BsonFactory extends ConvertFactory { return this.tiny; } + @Override + public BsonFactory nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + + protected boolean nullable() { + return this.nullable; + } + @Override public BsonFactory skipAllIgnore(final boolean skipIgnore) { this.registerSkipAllIgnore(skipIgnore); @@ -73,25 +85,26 @@ public final class BsonFactory extends ConvertFactory { } public static BsonFactory create() { - return new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", false)); + return new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true), + getSystemPropertyBoolean("redkale.convert.bson.nullable", "redkale.convert.nullable", false)); } @Override public final BsonConvert getConvert() { if (convert == null) { - convert = new BsonConvert(this, tiny); + convert = new BsonConvert(this, tiny, nullable); } return (BsonConvert) convert; } @Override public BsonFactory createChild() { - return new BsonFactory(this, this.tiny); + return new BsonFactory(this, this.tiny, this.nullable); } @Override - public BsonFactory createChild(boolean tiny) { - return new BsonFactory(this, tiny); + public BsonFactory createChild(boolean tiny, boolean nullable) { + return new BsonFactory(this, tiny, nullable); } @Override diff --git a/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java b/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java index 81c1da233..2d7a9b50f 100644 --- a/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java @@ -18,8 +18,8 @@ class BsonStreamWriter extends BsonByteBufferWriter { private OutputStream out; - protected BsonStreamWriter(boolean tiny, OutputStream out) { - super(tiny, null); + protected BsonStreamWriter(boolean tiny, boolean nullable, OutputStream out) { + super(tiny, nullable, null); this.out = out; } diff --git a/src/main/java/org/redkale/convert/bson/BsonWriter.java b/src/main/java/org/redkale/convert/bson/BsonWriter.java index 285dcfa4d..4d7227802 100644 --- a/src/main/java/org/redkale/convert/bson/BsonWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonWriter.java @@ -29,6 +29,8 @@ public class BsonWriter extends Writer implements ByteTuple { protected boolean tiny = BsonFactory.root().tiny(); + protected boolean nullable = BsonFactory.root().nullable(); + public static ObjectPool createPool(int max) { return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle()); } @@ -105,6 +107,16 @@ public class BsonWriter extends Writer implements ByteTuple { return this; } + @Override + public final boolean nullable() { + return nullable; + } + + public BsonWriter nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + //----------------------------------------------------------------------- //----------------------------------------------------------------------- /** diff --git a/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java index 80ce0fd22..fb83f04af 100644 --- a/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java @@ -34,12 +34,13 @@ public class JsonByteBufferWriter extends JsonWriter { private int index; - public JsonByteBufferWriter(boolean tiny, Supplier supplier) { - this(tiny, null, supplier); + public JsonByteBufferWriter(boolean tiny, boolean nullable, Supplier supplier) { + this(tiny, nullable, null, supplier); } - public JsonByteBufferWriter(boolean tiny, Charset charset, Supplier supplier) { + public JsonByteBufferWriter(boolean tiny, boolean nullable, Charset charset, Supplier supplier) { this.tiny = tiny; + this.nullable = nullable; this.charset = charset; this.supplier = supplier; } @@ -311,6 +312,10 @@ public class JsonByteBufferWriter extends JsonWriter { */ @Override public void writeLatin1To(final boolean quote, final String value) { + if (value == null) { + writeNull(); + return; + } byte[] bs = Utility.latin1ByteArray(value); int expandsize = expand(bs.length + (quote ? 2 : 0)); if (expandsize == 0) {// 只需要一个buffer @@ -564,6 +569,12 @@ public class JsonByteBufferWriter extends JsonWriter { @Override public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null && nullable()) { + writeTo(fieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { expand(1); this.buffers[index].put((byte) '}'); @@ -616,6 +627,13 @@ public class JsonByteBufferWriter extends JsonWriter { @Override //firstFieldBytes 必须带{开头 public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null && nullable()) { + writeTo('{'); + writeTo(firstFieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { int expandsize = expand(2); if (expandsize == 0) { // 只需要一个buffer diff --git a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java index d074913cc..c788e63f6 100644 --- a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java @@ -5,9 +5,9 @@ */ package org.redkale.convert.json; -import java.lang.reflect.*; -import java.nio.charset.*; -import java.util.function.*; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; import org.redkale.convert.*; import static org.redkale.convert.json.JsonWriter.*; import org.redkale.util.*; @@ -55,8 +55,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { this.count = array.length(); } - public JsonBytesWriter(boolean tiny, ByteArray array) { + public JsonBytesWriter(boolean tiny, boolean nullable, ByteArray array) { this.tiny = tiny; + this.nullable = nullable; this.content = array.content(); this.count = array.length(); } @@ -158,6 +159,10 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { */ @Override public void writeLatin1To(final boolean quote, final String value) { + if (value == null) { + writeNull(); + return; + } byte[] bs = Utility.latin1ByteArray(value); int len = bs.length; if (quote) { @@ -271,6 +276,12 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { @Override public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null && nullable()) { + writeTo(fieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { expand(1); content[count++] = '}'; @@ -294,6 +305,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { @Override //firstFieldBytes 必须带{开头 public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null && nullable()) { + writeTo('{'); + writeTo(firstFieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { expand(2); content[count++] = '{'; diff --git a/src/main/java/org/redkale/convert/json/JsonCharsWriter.java b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java index d6ea0f3b6..d753e79ab 100644 --- a/src/main/java/org/redkale/convert/json/JsonCharsWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java @@ -104,6 +104,10 @@ public class JsonCharsWriter extends JsonWriter { */ @Override public void writeLatin1To(final boolean quote, final String value) { + if (value == null) { + writeNull(); + return; + } int len = value.length(); expand(len + (quote ? 2 : 0)); if (quote) { @@ -220,6 +224,12 @@ public class JsonCharsWriter extends JsonWriter { @Override public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null && nullable()) { + writeTo(fieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { expand(1); content[count++] = '}'; @@ -241,6 +251,13 @@ public class JsonCharsWriter extends JsonWriter { @Override //firstFieldBytes 必须带{开头 public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null && nullable()) { + writeTo('{'); + writeTo(firstFieldBytes); + writeNull(); + writeTo('}'); + return; + } if (value == null || (tiny && value.isEmpty())) { expand(2); content[count++] = '{'; diff --git a/src/main/java/org/redkale/convert/json/JsonConvert.java b/src/main/java/org/redkale/convert/json/JsonConvert.java index 360ff76d7..2bb98c88e 100644 --- a/src/main/java/org/redkale/convert/json/JsonConvert.java +++ b/src/main/java/org/redkale/convert/json/JsonConvert.java @@ -42,13 +42,16 @@ public class JsonConvert extends TextConvert { private final boolean tiny; + private final boolean nullable; + private Encodeable lastConvertEncodeable; private Decodeable lastConvertDecodeable; - protected JsonConvert(JsonFactory factory, boolean tiny) { + protected JsonConvert(JsonFactory factory, boolean tiny, boolean nullable) { super(factory); this.tiny = tiny; + this.nullable = nullable; } @Override @@ -77,7 +80,7 @@ public class JsonConvert extends TextConvert { @Override public JsonConvert newConvert(final BiFunction objFieldFunc, BiFunction mapFieldFunc, Function objExtFunc) { - return new JsonConvert(getFactory(), tiny) { + return new JsonConvert(getFactory(), tiny, nullable) { @Override protected S configWrite(S writer) { return fieldFunc(writer, objFieldFunc, mapFieldFunc, objExtFunc); @@ -112,7 +115,7 @@ public class JsonConvert extends TextConvert { } else { bytesWriterPool.set(null); } - return configWrite((JsonBytesWriter) writer.tiny(tiny)); + return configWrite((JsonBytesWriter) writer.tiny(tiny).nullable(nullable)); } @Override @@ -132,7 +135,7 @@ public class JsonConvert extends TextConvert { } else { bytesWriterPool.set(null); } - return configWrite((JsonBytesWriter) writer.tiny(tiny)); + return configWrite((JsonBytesWriter) writer.tiny(tiny).nullable(nullable)); } private void offerJsonBytesWriter(final JsonBytesWriter writer) { @@ -392,7 +395,7 @@ public class JsonConvert extends TextConvert { @Override public void convertToBytes(final ByteArray array, final Type type, final Object value) { Objects.requireNonNull(array); - JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array)); + JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, nullable, array)); if (value == null) { writer.writeNull(); } else { @@ -412,10 +415,10 @@ public class JsonConvert extends TextConvert { public void convertTo(final OutputStream out, final Type type, final Object value) { if (value == null) { - configWrite(new JsonStreamWriter(tiny, out)).writeNull(); + configWrite(new JsonStreamWriter(tiny, nullable, out)).writeNull(); } else { final Type t = type == null ? value.getClass() : type; - JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out)); + JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, nullable, out)); Encodeable encoder = this.lastConvertEncodeable; if (encoder == null || encoder.getType() != t) { encoder = factory.loadEncoder(t); @@ -431,7 +434,7 @@ public class JsonConvert extends TextConvert { @Override public ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value) { Objects.requireNonNull(supplier); - JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier)); + JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, nullable, supplier)); if (value == null) { out.writeNull(); } else { diff --git a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java index 33832efd3..99383518f 100644 --- a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java +++ b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java @@ -386,7 +386,7 @@ public abstract class JsonDynEncoder implements Encodeable { final Map mixedNames = mixedNames0; final ClassLoader loader = Thread.currentThread().getContextClassLoader(); final String newDynName = "org/redkaledyn/json/_Dyn" + JsonDynEncoder.class.getSimpleName() - + "__" + clazz.getName().replace('.', '_').replace('$', '_') + "_" + factory.tiny() + "_" + Utility.md5Hex(memberb.toString()); //tiny必须要加上, 同一个类会有多个字段定制Convert + + "__" + clazz.getName().replace('.', '_').replace('$', '_') + "_" + factory.tiny() + "_" + factory.nullable() + "_" + Utility.md5Hex(memberb.toString()); //tiny必须要加上, 同一个类会有多个字段定制Convert try { Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz; @@ -512,7 +512,7 @@ public abstract class JsonDynEncoder implements Encodeable { int maxLocals = 4; int elementIndex = -1; final Class firstType = readGetSetFieldType(members.get(0)); - final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny()); //byte/short/char/int/float/long/double + final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny() || factory.nullable()); //byte/short/char/int/float/long/double if (onlyOneLatin1FieldObjectFlag) { //out.writeObjectByOnlyOneLatin1FieldValue(messageFirstFieldBytes, value.getMessage());elementIndex++; @@ -670,7 +670,7 @@ public abstract class JsonDynEncoder implements Encodeable { } } Label msgnotemptyif = null; - if (!fieldtype.isPrimitive()) { //if (message != null) { start + if (!fieldtype.isPrimitive() && !factory.nullable()) { //if (message != null) { start mv.visitVarInsn(loadid, maxLocals); msgnotemptyif = new Label(); mv.visitJumpInsn(IFNULL, msgnotemptyif); @@ -794,7 +794,7 @@ public abstract class JsonDynEncoder implements Encodeable { mv.visitVarInsn(loadid, maxLocals); mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true); } - if (!fieldtype.isPrimitive()) { //if (message != null) } end + if (!fieldtype.isPrimitive() && !factory.nullable()) { //if (message != null) } end mv.visitLabel(msgnotemptyif); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } else if (fieldtype == boolean.class && factory.tiny()) { diff --git a/src/main/java/org/redkale/convert/json/JsonFactory.java b/src/main/java/org/redkale/convert/json/JsonFactory.java index 06fef52f0..43a94faf1 100644 --- a/src/main/java/org/redkale/convert/json/JsonFactory.java +++ b/src/main/java/org/redkale/convert/json/JsonFactory.java @@ -24,7 +24,9 @@ import org.redkale.util.Uint128; @SuppressWarnings("unchecked") public final class JsonFactory extends ConvertFactory { - private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false)); + private static final JsonFactory instance = new JsonFactory(null, + getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false), + getSystemPropertyBoolean("redkale.convert.json.nullable", "redkale.convert.nullable", false)); static { instance.register(Serializable.class, instance.loadEncoder(Object.class)); @@ -33,8 +35,8 @@ public final class JsonFactory extends ConvertFactory { //instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); } - private JsonFactory(JsonFactory parent, boolean tiny) { - super(parent, tiny); + private JsonFactory(JsonFactory parent, boolean tiny, boolean nullable) { + super(parent, tiny, nullable); if (parent == null) { this.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance); this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance); @@ -59,6 +61,12 @@ public final class JsonFactory extends ConvertFactory { return this; } + @Override + public JsonFactory nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + @Override public JsonFactory skipAllIgnore(final boolean skipIgnore) { this.registerSkipAllIgnore(skipIgnore); @@ -70,7 +78,8 @@ public final class JsonFactory extends ConvertFactory { } public static JsonFactory create() { - return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false)); + return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "redkale.convert.tiny", false), + getSystemPropertyBoolean("redkale.convert.json.nullable", "redkale.convert.nullable", false)); } @Override @@ -92,22 +101,26 @@ public final class JsonFactory extends ConvertFactory { return this.tiny; } + protected boolean nullable() { + return this.nullable; + } + @Override public final JsonConvert getConvert() { if (convert == null) { - convert = new JsonConvert(this, tiny); + convert = new JsonConvert(this, tiny, nullable); } return (JsonConvert) convert; } @Override public JsonFactory createChild() { - return new JsonFactory(this, this.tiny); + return new JsonFactory(this, this.tiny, this.nullable); } @Override - public JsonFactory createChild(boolean tiny) { - return new JsonFactory(this, tiny); + public JsonFactory createChild(boolean tiny, boolean nullable) { + return new JsonFactory(this, tiny, nullable); } @Override diff --git a/src/main/java/org/redkale/convert/json/JsonStreamWriter.java b/src/main/java/org/redkale/convert/json/JsonStreamWriter.java index c2405609c..c1de22503 100644 --- a/src/main/java/org/redkale/convert/json/JsonStreamWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonStreamWriter.java @@ -21,12 +21,12 @@ class JsonStreamWriter extends JsonByteBufferWriter { private OutputStream out; - protected JsonStreamWriter(boolean tiny, OutputStream out) { - this(tiny, null, out); + protected JsonStreamWriter(boolean tiny, boolean nullable, OutputStream out) { + this(tiny, nullable, null, out); } - protected JsonStreamWriter(boolean tiny, Charset charset, OutputStream out) { - super(tiny, charset, null); + protected JsonStreamWriter(boolean tiny, boolean nullable, Charset charset, OutputStream out) { + super(tiny, nullable, charset, null); this.out = out; } @@ -101,6 +101,10 @@ class JsonStreamWriter extends JsonByteBufferWriter { */ @Override public void writeLatin1To(final boolean quote, final String value) { + if (value == null) { + writeNull(); + return; + } char[] chs = Utility.charArray(value); writeTo(quote, chs, 0, chs.length); } diff --git a/src/main/java/org/redkale/convert/json/JsonWriter.java b/src/main/java/org/redkale/convert/json/JsonWriter.java index ee3e47291..6e54415ac 100644 --- a/src/main/java/org/redkale/convert/json/JsonWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonWriter.java @@ -23,6 +23,8 @@ public abstract class JsonWriter extends Writer { protected boolean tiny = JsonFactory.root().tiny(); + protected boolean nullable = JsonFactory.root().nullable(); + @Override public boolean tiny() { return tiny; @@ -37,6 +39,16 @@ public abstract class JsonWriter extends Writer { return this.objExtFunc == null && this.objFieldFunc == null; } + @Override + public boolean nullable() { + return nullable; + } + + public JsonWriter nullable(boolean nullable) { + this.nullable = nullable; + return this; + } + //----------------------------------------------------------------------- public abstract void writeTo(final char ch); //只能是 0 - 127 的字符 diff --git a/src/test/java/org/redkale/test/convert/BsonMainTest.java b/src/test/java/org/redkale/test/convert/BsonMainTest.java index 0b067e51e..4004843da 100644 --- a/src/test/java/org/redkale/test/convert/BsonMainTest.java +++ b/src/test/java/org/redkale/test/convert/BsonMainTest.java @@ -20,6 +20,19 @@ import org.redkale.util.*; */ public class BsonMainTest { + + public static void main(String[] args) throws Throwable { + BsonMainTest test = new BsonMainTest(); + test.run1(); + test.run2(); + test.run3(); + test.run4(); + test.run5(); + test.run6(); + test.run7(); + test.run8(); + } + @Test public void run1() throws Throwable { Serializable[] sers = new Serializable[]{"aaa", 4}; @@ -38,7 +51,7 @@ public class BsonMainTest { SimpleChildEntity entry = SimpleChildEntity.create(); byte[] bytes = convert.convertTo(SimpleEntity.class, entry); System.out.println("长度: " + bytes.length); - Assertions.assertEquals(271, bytes.length); + Assertions.assertEquals(260, bytes.length); BsonByteBufferWriter writer = convert.pollWriter(() -> ByteBuffer.allocate(1)); convert.convertTo(writer, SimpleEntity.class, entry); ByteBuffer[] buffers = writer.toBuffers(); @@ -52,7 +65,7 @@ public class BsonMainTest { b.flip(); } System.out.println("长度: " + len); - Assertions.assertEquals(271, len); + Assertions.assertEquals(260, len); SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, buffers); System.out.println(entry); Assertions.assertEquals(entry.toString(), entry2.toString()); diff --git a/src/test/java/org/redkale/test/convert/Json5Test.java b/src/test/java/org/redkale/test/convert/Json5Test.java index f3dbe1777..4302ecd85 100644 --- a/src/test/java/org/redkale/test/convert/Json5Test.java +++ b/src/test/java/org/redkale/test/convert/Json5Test.java @@ -17,11 +17,21 @@ public class Json5Test { public static void main(String[] args) throws Throwable { Json5Test test = new Json5Test(); test.main = true; - test.run(); + test.run1(); + test.run2(); } @Test - public void run() throws Exception { + public void run1() throws Exception { + JsonFactory factory = JsonFactory.root().tiny(true).nullable(true); + final JsonConvert convert = factory.getConvert(); + Json5Bean bean = new Json5Bean(); + bean.id = 60; + System.out.println(convert.convertTo(bean)); + } + + @Test + public void run2() throws Exception { JsonConvert convert = JsonConvert.root(); Json5Bean bean = new Json5Bean(); bean.id = 500; diff --git a/src/test/java/org/redkale/test/convert/JsonMainTest.java b/src/test/java/org/redkale/test/convert/JsonMainTest.java index f36ff6af6..aca35ba2c 100644 --- a/src/test/java/org/redkale/test/convert/JsonMainTest.java +++ b/src/test/java/org/redkale/test/convert/JsonMainTest.java @@ -37,6 +37,7 @@ public class JsonMainTest { String json = "{\"access_token\":\"null\",\"priv\":null, vvv:nulla,\"priv2\":\"nulla\",\"expires_in\":7200, \"aa\":\"\"}"; Map map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, json); System.out.println(map); + System.out.println(map.get("priv") == null); String rs = convert.convertTo(map); System.out.println(rs); ByteBuffer[] buffers = convert.convertTo(() -> ByteBuffer.allocate(1024), map); @@ -52,7 +53,7 @@ public class JsonMainTest { SimpleChildEntity entry = SimpleChildEntity.create(); String json = convert.convertTo(SimpleEntity.class, entry); System.out.println("长度: " + json.length()); - JsonByteBufferWriter writer = new JsonByteBufferWriter(false, () -> ByteBuffer.allocate(1)) { + JsonByteBufferWriter writer = new JsonByteBufferWriter(false, false, () -> ByteBuffer.allocate(1)) { }; convert.convertTo(writer, SimpleEntity.class, entry); ByteBuffer[] buffers = writer.toBuffers();