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) {
Node root = new Node(JsonType.OBJECT);
for (FieldSchema field : schema.getFields().values()) {
putPath(root, field.getPath(), field.getJsonType());
putPath(root, field.getPath(), field.getJsonType(), field.getJavaType());
}
// 根数组:字段以 [] / [].xxx 记录
if (root.children.size() == 1 && root.children.containsKey("[]")) {
@@ -56,7 +56,7 @@ public class SkeletonJsonRenderer {
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);
if (segs.isEmpty()) {
return;
@@ -70,10 +70,9 @@ public class SkeletonJsonRenderer {
arr.type = JsonType.ARRAY;
Node elem = arr.children.computeIfAbsent("[]", k -> new Node(JsonType.OBJECT));
if (last) {
if (type == JsonType.OBJECT || type == JsonType.ARRAY || type == JsonType.MAP) {
elem.type = type;
} else {
elem.type = type;
elem.javaType = javaType;
if (type != JsonType.OBJECT && type != JsonType.ARRAY && type != JsonType.MAP) {
elem.leaf = true;
}
}
@@ -83,6 +82,7 @@ public class SkeletonJsonRenderer {
k -> new Node(last ? type : JsonType.OBJECT));
if (last) {
child.type = type;
child.javaType = javaType;
child.leaf = type != JsonType.OBJECT && type != JsonType.ARRAY && type != JsonType.MAP;
} else if (child.type != JsonType.ARRAY) {
child.type = JsonType.OBJECT;
@@ -129,7 +129,7 @@ public class SkeletonJsonRenderer {
return "[" + write(elem, elemPath, protectedPaths, compact) + "]";
}
if (node.leaf || isScalar(node.type)) {
return placeholder(node.type);
return placeholder(node.type, node.javaType);
}
if (node.type == JsonType.MAP) {
return "{}";
@@ -161,7 +161,7 @@ public class SkeletonJsonRenderer {
} else if (child.type == JsonType.ARRAY) {
sb.append(write(child, childPath, protectedPaths, compact));
} else if (child.leaf || isScalar(child.type)) {
sb.append(placeholder(child.type));
sb.append(placeholder(child.type, child.javaType));
} else {
sb.append(write(child, childPath, protectedPaths, compact));
}
@@ -177,7 +177,7 @@ public class SkeletonJsonRenderer {
if (child.type == JsonType.OBJECT || child.type == JsonType.MAP) {
return "\"...\"";
}
return placeholder(child.type);
return placeholder(child.type, child.javaType);
}
private boolean isProtectedUnder(String pathPrefix, Set<String> protectedPaths) {
@@ -327,13 +327,17 @@ public class SkeletonJsonRenderer {
|| 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) {
return "null";
}
switch (type) {
case NUMBER:
return "0";
return numberPlaceholder(javaType);
case BOOLEAN:
return "false";
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) {
return s.replace("\\", "\\\\").replace("\"", "\\\"");
}
@@ -365,6 +400,7 @@ public class SkeletonJsonRenderer {
private static final class Node {
JsonType type;
String javaType;
boolean leaf;
final Map<String, Node> children = new LinkedHashMap<>();

View File

@@ -80,4 +80,31 @@ class SkeletonJsonRendererTest {
"截断后仍应保留改动相关字段: " + truncated);
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));
}
}