diff --git a/src/main/java/com/codechecker/cache/schema/SkeletonJsonRenderer.java b/src/main/java/com/codechecker/cache/schema/SkeletonJsonRenderer.java index 4d57c04..17c0758 100644 --- a/src/main/java/com/codechecker/cache/schema/SkeletonJsonRenderer.java +++ b/src/main/java/com/codechecker/cache/schema/SkeletonJsonRenderer.java @@ -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 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.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 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 children = new LinkedHashMap<>(); diff --git a/src/test/java/com/codechecker/cache/schema/SkeletonJsonRendererTest.java b/src/test/java/com/codechecker/cache/schema/SkeletonJsonRendererTest.java index caf8d8b..0b0c3ae 100644 --- a/src/test/java/com/codechecker/cache/schema/SkeletonJsonRendererTest.java +++ b/src/test/java/com/codechecker/cache/schema/SkeletonJsonRendererTest.java @@ -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)); + } }