feat: int/float/decimal精度优化
All checks were successful
序列化结构检查 / serialization-schema-check (push) Has been skipped

This commit is contained in:
2026-07-15 14:03:24 +08:00
parent 27dfeb8a4c
commit a03e1b4819
2 changed files with 74 additions and 11 deletions

View File

@@ -45,7 +45,7 @@ public class SkeletonJsonRenderer {
private Node buildTree(TypeSchema schema) { private Node buildTree(TypeSchema schema) {
Node root = new Node(JsonType.OBJECT); Node root = new Node(JsonType.OBJECT);
for (FieldSchema field : schema.getFields().values()) { for (FieldSchema field : schema.getFields().values()) {
putPath(root, field.getPath(), field.getJsonType()); putPath(root, field.getPath(), field.getJsonType(), field.getJavaType());
} }
// 根数组:字段以 [] / [].xxx 记录 // 根数组:字段以 [] / [].xxx 记录
if (root.children.size() == 1 && root.children.containsKey("[]")) { if (root.children.size() == 1 && root.children.containsKey("[]")) {
@@ -56,7 +56,7 @@ public class SkeletonJsonRenderer {
return root; return root;
} }
private void putPath(Node root, String path, JsonType type) { private void putPath(Node root, String path, JsonType type, String javaType) {
List<Seg> segs = parsePath(path); List<Seg> segs = parsePath(path);
if (segs.isEmpty()) { if (segs.isEmpty()) {
return; return;
@@ -70,10 +70,9 @@ public class SkeletonJsonRenderer {
arr.type = JsonType.ARRAY; arr.type = JsonType.ARRAY;
Node elem = arr.children.computeIfAbsent("[]", k -> new Node(JsonType.OBJECT)); Node elem = arr.children.computeIfAbsent("[]", k -> new Node(JsonType.OBJECT));
if (last) { if (last) {
if (type == JsonType.OBJECT || type == JsonType.ARRAY || type == JsonType.MAP) {
elem.type = type;
} else {
elem.type = type; elem.type = type;
elem.javaType = javaType;
if (type != JsonType.OBJECT && type != JsonType.ARRAY && type != JsonType.MAP) {
elem.leaf = true; elem.leaf = true;
} }
} }
@@ -83,6 +82,7 @@ public class SkeletonJsonRenderer {
k -> new Node(last ? type : JsonType.OBJECT)); k -> new Node(last ? type : JsonType.OBJECT));
if (last) { if (last) {
child.type = type; child.type = type;
child.javaType = javaType;
child.leaf = type != JsonType.OBJECT && type != JsonType.ARRAY && type != JsonType.MAP; child.leaf = type != JsonType.OBJECT && type != JsonType.ARRAY && type != JsonType.MAP;
} else if (child.type != JsonType.ARRAY) { } else if (child.type != JsonType.ARRAY) {
child.type = JsonType.OBJECT; child.type = JsonType.OBJECT;
@@ -129,7 +129,7 @@ public class SkeletonJsonRenderer {
return "[" + write(elem, elemPath, protectedPaths, compact) + "]"; return "[" + write(elem, elemPath, protectedPaths, compact) + "]";
} }
if (node.leaf || isScalar(node.type)) { if (node.leaf || isScalar(node.type)) {
return placeholder(node.type); return placeholder(node.type, node.javaType);
} }
if (node.type == JsonType.MAP) { if (node.type == JsonType.MAP) {
return "{}"; return "{}";
@@ -161,7 +161,7 @@ public class SkeletonJsonRenderer {
} else if (child.type == JsonType.ARRAY) { } else if (child.type == JsonType.ARRAY) {
sb.append(write(child, childPath, protectedPaths, compact)); sb.append(write(child, childPath, protectedPaths, compact));
} else if (child.leaf || isScalar(child.type)) { } else if (child.leaf || isScalar(child.type)) {
sb.append(placeholder(child.type)); sb.append(placeholder(child.type, child.javaType));
} else { } else {
sb.append(write(child, childPath, protectedPaths, compact)); sb.append(write(child, childPath, protectedPaths, compact));
} }
@@ -177,7 +177,7 @@ public class SkeletonJsonRenderer {
if (child.type == JsonType.OBJECT || child.type == JsonType.MAP) { if (child.type == JsonType.OBJECT || child.type == JsonType.MAP) {
return "\"...\""; return "\"...\"";
} }
return placeholder(child.type); return placeholder(child.type, child.javaType);
} }
private boolean isProtectedUnder(String pathPrefix, Set<String> protectedPaths) { private boolean isProtectedUnder(String pathPrefix, Set<String> protectedPaths) {
@@ -327,13 +327,17 @@ public class SkeletonJsonRenderer {
|| type == JsonType.BOOLEAN || type == JsonType.UNKNOWN; || type == JsonType.BOOLEAN || type == JsonType.UNKNOWN;
} }
private String placeholder(JsonType type) { /**
* 数字类型示意占位(合法 JSON number整数 {@code 0}、浮点 {@code 0.0}、BigDecimal {@code 0.00}。
* 仅用于通知可读性,不代表真实精度/scale。
*/
private String placeholder(JsonType type, String javaType) {
if (type == null) { if (type == null) {
return "null"; return "null";
} }
switch (type) { switch (type) {
case NUMBER: case NUMBER:
return "0"; return numberPlaceholder(javaType);
case BOOLEAN: case BOOLEAN:
return "false"; return "false";
case STRING: case STRING:
@@ -349,6 +353,37 @@ public class SkeletonJsonRenderer {
} }
} }
private String numberPlaceholder(String javaType) {
String simple = simpleJavaType(javaType);
if ("BigDecimal".equals(simple)) {
return "0.00";
}
if ("float".equals(simple) || "Float".equals(simple)
|| "double".equals(simple) || "Double".equals(simple)) {
return "0.0";
}
return "0";
}
private static String simpleJavaType(String javaType) {
if (javaType == null) {
return "";
}
String s = javaType.trim();
if (s.isEmpty()) {
return "";
}
int lt = s.indexOf('<');
if (lt > 0) {
s = s.substring(0, lt).trim();
}
int dot = s.lastIndexOf('.');
if (dot >= 0 && dot < s.length() - 1) {
s = s.substring(dot + 1);
}
return s;
}
private String escape(String s) { private String escape(String s) {
return s.replace("\\", "\\\\").replace("\"", "\\\""); return s.replace("\\", "\\\\").replace("\"", "\\\"");
} }
@@ -365,6 +400,7 @@ public class SkeletonJsonRenderer {
private static final class Node { private static final class Node {
JsonType type; JsonType type;
String javaType;
boolean leaf; boolean leaf;
final Map<String, Node> children = new LinkedHashMap<>(); final Map<String, Node> children = new LinkedHashMap<>();

View File

@@ -80,4 +80,31 @@ class SkeletonJsonRendererTest {
"截断后仍应保留改动相关字段: " + truncated); "截断后仍应保留改动相关字段: " + truncated);
assertEquals(true, truncated.length() >= 10); assertEquals(true, truncated.length() >= 10);
} }
@Test
void numberPlaceholdersDistinguishIntegerFloatAndBigDecimal() {
TypeSchema schema = new TypeSchema("MoneyVo");
schema.add(new FieldSchema("count", JsonType.NUMBER, "Integer"));
schema.add(new FieldSchema("rate", JsonType.NUMBER, "Double"));
schema.add(new FieldSchema("amount", JsonType.NUMBER, "BigDecimal"));
schema.add(new FieldSchema("score", JsonType.NUMBER, "Float"));
String json = new SkeletonJsonRenderer().render(schema);
assertTrue(json.contains("\"count\":0"), json);
assertTrue(json.contains("\"rate\":0.0"), json);
assertTrue(json.contains("\"amount\":0.00"), json);
assertTrue(json.contains("\"score\":0.0"), json);
}
@Test
void integerToBigDecimalSkeletonsLookDifferent() {
TypeSchema oldS = new TypeSchema("A");
oldS.add(new FieldSchema("amount", JsonType.NUMBER, "Integer"));
TypeSchema newS = new TypeSchema("A");
newS.add(new FieldSchema("amount", JsonType.NUMBER, "BigDecimal"));
SkeletonJsonRenderer renderer = new SkeletonJsonRenderer();
assertEquals("{\"amount\":0}", renderer.render(oldS));
assertEquals("{\"amount\":0.00}", renderer.render(newS));
}
} }