Skip to content

Commit d78bd45

Browse files
committed
support forbid unknown fields
1 parent 627e6f2 commit d78bd45

14 files changed

Lines changed: 86 additions & 37 deletions

src/main/java/com/jsoniter/Any.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ private static Object getPath(Object val, Object... keys) {
222222
System.arraycopy(keys, 1, nextKeys, 0, nextKeys.length);
223223
return getPath(nextVal, nextKeys);
224224
}
225-
throw new RuntimeException("invalid key type: " + key);
225+
throw new JsonException("invalid key type: " + key);
226226
}
227227

228228
private static Object getFromMap(Object val, String key) {

src/main/java/com/jsoniter/Codegen.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private synchronized static Decoder gen(String cacheKey, Type type) {
7070
} catch (Exception e) {
7171
System.err.println("failed to generate decoder for: " + type + " with " + Arrays.toString(typeArgs));
7272
System.err.println(source);
73-
throw new RuntimeException(e);
73+
throw new JsonException(e);
7474
}
7575
}
7676

@@ -88,6 +88,9 @@ private static String genSource(String cacheKey, Class clazz, Type[] typeArgs) {
8888
return CodegenImplArray.genCollection(clazz, typeArgs);
8989
}
9090
ClassDescriptor desc = ExtensionManager.getClassDescriptor(clazz, false);
91+
if (desc.forbidUnknownFields) {
92+
return CodegenImplObject.genObjectUsingSlice(clazz, cacheKey, desc);
93+
}
9194
if (desc.allDecoderBindings().isEmpty()) {
9295
return CodegenImplObject.genObjectUsingSkip(clazz, desc.ctor);
9396
}

src/main/java/com/jsoniter/CodegenImplNative.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public static String genNative(String nativeReadKey) {
5555
}
5656
String op = NATIVE_READS.get(nativeReadKey);
5757
if (op == null) {
58-
throw new RuntimeException("do not know how to read: " + nativeReadKey);
58+
throw new JsonException("do not know how to read: " + nativeReadKey);
5959
}
6060
StringBuilder lines = new StringBuilder();
6161
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
@@ -95,49 +95,49 @@ public static String genField(Binding field, String cacheKey) {
9595
}
9696
if (fieldType == boolean.class) {
9797
if (!(decoder instanceof Decoder.BooleanDecoder)) {
98-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.BooleanDecoder");
98+
throw new JsonException("decoder for field " + field + "must implement Decoder.BooleanDecoder");
9999
}
100100
return String.format("com.jsoniter.CodegenAccess.readBoolean(\"%s\", iter)", fieldCacheKey);
101101
}
102102
if (fieldType == byte.class) {
103103
if (!(decoder instanceof Decoder.ShortDecoder)) {
104-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.ShortDecoder");
104+
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
105105
}
106106
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
107107
}
108108
if (fieldType == short.class) {
109109
if (!(decoder instanceof Decoder.ShortDecoder)) {
110-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.ShortDecoder");
110+
throw new JsonException("decoder for field " + field + "must implement Decoder.ShortDecoder");
111111
}
112112
return String.format("com.jsoniter.CodegenAccess.readShort(\"%s\", iter)", fieldCacheKey);
113113
}
114114
if (fieldType == char.class) {
115115
if (!(decoder instanceof Decoder.IntDecoder)) {
116-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.IntDecoder");
116+
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
117117
}
118118
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
119119
}
120120
if (fieldType == int.class) {
121121
if (!(decoder instanceof Decoder.IntDecoder)) {
122-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.IntDecoder");
122+
throw new JsonException("decoder for field " + field + "must implement Decoder.IntDecoder");
123123
}
124124
return String.format("com.jsoniter.CodegenAccess.readInt(\"%s\", iter)", fieldCacheKey);
125125
}
126126
if (fieldType == long.class) {
127127
if (!(decoder instanceof Decoder.LongDecoder)) {
128-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.LongDecoder");
128+
throw new JsonException("decoder for field " + field + "must implement Decoder.LongDecoder");
129129
}
130130
return String.format("com.jsoniter.CodegenAccess.readLong(\"%s\", iter)", fieldCacheKey);
131131
}
132132
if (fieldType == float.class) {
133133
if (!(decoder instanceof Decoder.FloatDecoder)) {
134-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.FloatDecoder");
134+
throw new JsonException("decoder for field " + field + "must implement Decoder.FloatDecoder");
135135
}
136136
return String.format("com.jsoniter.CodegenAccess.readFloat(\"%s\", iter)", fieldCacheKey);
137137
}
138138
if (fieldType == double.class) {
139139
if (!(decoder instanceof Decoder.DoubleDecoder)) {
140-
throw new RuntimeException("decoder for field " + field + "must implement Decoder.DoubleDecoder");
140+
throw new JsonException("decoder for field " + field + "must implement Decoder.DoubleDecoder");
141141
}
142142
return String.format("com.jsoniter.CodegenAccess.readDouble(\"%s\", iter)", fieldCacheKey);
143143
}
@@ -155,7 +155,7 @@ public static String getTypeName(Type fieldType) {
155155
Class clazz = (Class) pType.getRawType();
156156
return clazz.getCanonicalName();
157157
} else {
158-
throw new RuntimeException("unsupported type: " + fieldType);
158+
throw new JsonException("unsupported type: " + fieldType);
159159
}
160160
}
161161

src/main/java/com/jsoniter/CodegenImplObject.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class CodegenImplObject {
2424

2525
public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDescriptor desc) {
2626
// TODO: when setter is single argument, decode like field
27-
Map<Integer, Object> trieTree = buildTriTree(desc.allDecoderBindings());
27+
List<Binding> allBindings = desc.allDecoderBindings();
28+
Map<Integer, Object> trieTree = buildTriTree(allBindings);
2829
StringBuilder lines = new StringBuilder();
2930
append(lines, "public static Object decode_(com.jsoniter.JsonIterator iter) {");
3031
// if null, return null
@@ -51,7 +52,6 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
5152
append(lines, "boolean once = true;");
5253
append(lines, "while (once) {");
5354
append(lines, "once = false;");
54-
append(lines, "switch (field.len) {");
5555
String rendered = renderTriTree(cacheKey, trieTree);
5656
for (Binding field : desc.fields) {
5757
if (desc.ctor.parameters.isEmpty() && desc.fields.contains(field)) {
@@ -63,16 +63,21 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
6363
}
6464
}
6565
}
66-
append(lines, rendered);
67-
append(lines, "}"); // end of switch
68-
append(lines, "iter.skip();");
66+
if (!desc.ctor.parameters.isEmpty()) {
67+
append(lines, "switch (field.len) {");
68+
append(lines, rendered);
69+
append(lines, "}"); // end of switch
70+
}
71+
appendOnUnknownField(lines, desc);
6972
append(lines, "}"); // end of while
7073
append(lines, "while (com.jsoniter.CodegenAccess.nextToken(iter) == ',') {");
7174
append(lines, "field = com.jsoniter.CodegenAccess.readObjectFieldAsSlice(iter);");
72-
append(lines, "switch (field.len) {");
73-
append(lines, rendered);
74-
append(lines, "}"); // end of switch
75-
append(lines, "iter.skip();");
75+
if (!allBindings.isEmpty()) {
76+
append(lines, "switch (field.len) {");
77+
append(lines, rendered);
78+
append(lines, "}"); // end of switch
79+
}
80+
appendOnUnknownField(lines, desc);
7681
append(lines, "}"); // end of while
7782
if (!desc.ctor.parameters.isEmpty()) {
7883
append(lines, String.format("%s obj = {{newInst}};", CodegenImplNative.getTypeName(clazz)));
@@ -88,6 +93,14 @@ public static String genObjectUsingSlice(Class clazz, String cacheKey, ClassDesc
8893
.replace("{{newInst}}", genNewInstCode(clazz, desc.ctor));
8994
}
9095

96+
private static void appendOnUnknownField(StringBuilder lines, ClassDescriptor desc) {
97+
if (desc.forbidUnknownFields) {
98+
append(lines, "throw new com.jsoniter.JsonException('unknown field: ' + field.toString());".replace('\'', '"'));
99+
} else {
100+
append(lines, "iter.skip();");
101+
}
102+
}
103+
91104
private static String renderTriTree(String cacheKey, Map<Integer, Object> trieTree) {
92105
StringBuilder switchBody = new StringBuilder();
93106
for (Map.Entry<Integer, Object> entry : trieTree.entrySet()) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.jsoniter;
2+
3+
public class JsonException extends RuntimeException {
4+
public JsonException() {
5+
}
6+
7+
public JsonException(String message) {
8+
super(message);
9+
}
10+
11+
public JsonException(String message, Throwable cause) {
12+
super(message, cause);
13+
}
14+
15+
public JsonException(Throwable cause) {
16+
super(cause);
17+
}
18+
19+
public JsonException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
20+
super(message, cause, enableSuppression, writableStackTrace);
21+
}
22+
}

src/main/java/com/jsoniter/JsonIterator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,13 @@ final void unreadByte() throws IOException {
124124
head--;
125125
}
126126

127-
public final RuntimeException reportError(String op, String msg) {
127+
public final JsonException reportError(String op, String msg) {
128128
int peekStart = head - 10;
129129
if (peekStart < 0) {
130130
peekStart = 0;
131131
}
132132
String peek = new String(buf, peekStart, head - peekStart);
133-
throw new RuntimeException(op + ": " + msg + ", head: " + head + ", peek: " + peek + ", buf: " + new String(buf));
133+
throw new JsonException(op + ": " + msg + ", head: " + head + ", peek: " + peek + ", buf: " + new String(buf));
134134
}
135135

136136
public final String currentBuffer() {
@@ -171,7 +171,7 @@ public final short readShort() throws IOException {
171171
if (Short.MIN_VALUE <= v && v <= Short.MAX_VALUE) {
172172
return (short) v;
173173
} else {
174-
throw new RuntimeException("short overflow: " + v);
174+
throw new JsonException("short overflow: " + v);
175175
}
176176
}
177177

src/main/java/com/jsoniter/ReflectionDecoder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public ReflectionDecoder(Class clazz) {
3131
try {
3232
init(clazz);
3333
} catch (Exception e) {
34-
throw new RuntimeException(e);
34+
throw new JsonException(e);
3535
}
3636
}
3737

@@ -75,7 +75,7 @@ private int addBinding(Class clazz, int tempIdx, Binding param) {
7575
param.idx = tempIdx;
7676
for (String fromName : param.fromNames) {
7777
if (allBindings.containsKey(fromName)) {
78-
throw new RuntimeException("name conflict found in " + clazz +": " + fromName);
78+
throw new JsonException("name conflict found in " + clazz +": " + fromName);
7979
}
8080
allBindings.put(fromName, param);
8181
}
@@ -96,7 +96,7 @@ public final Object decode(JsonIterator iter) throws IOException {
9696
return decodeWithCtorBinding(iter);
9797
}
9898
} catch (Exception e) {
99-
throw new RuntimeException(e);
99+
throw new JsonException(e);
100100
}
101101
}
102102

src/main/java/com/jsoniter/TypeLiteral.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ private static String generateCacheKey(Type type, String prefix) {
3838
if (type instanceof Class) {
3939
Class clazz = (Class) type;
4040
if (clazz.isAnonymousClass()) {
41-
throw new RuntimeException("anonymous class not supported: " + clazz);
41+
throw new JsonException("anonymous class not supported: " + clazz);
4242
}
4343
decoderClassName.append(clazz.getCanonicalName().replace("[]", "_array"));
4444
} else if (type instanceof ParameterizedType) {
@@ -70,13 +70,13 @@ private static String formatTypeWithoutSpecialCharacter(Type type) {
7070
}
7171
return typeName;
7272
}
73-
throw new RuntimeException("unsupported type: " + type);
73+
throw new JsonException("unsupported type: " + type);
7474
}
7575

7676
static Type getSuperclassTypeParameter(Class<?> subclass) {
7777
Type superclass = subclass.getGenericSuperclass();
7878
if (superclass instanceof Class) {
79-
throw new RuntimeException("Missing type parameter.");
79+
throw new JsonException("Missing type parameter.");
8080
}
8181
ParameterizedType parameterized = (ParameterizedType) superclass;
8282
return parameterized.getActualTypeArguments()[0];

src/main/java/com/jsoniter/annotation/JsoniterAnnotationSupport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void updateClassDescriptor(ClassDescriptor desc) {
4444
Annotation[] paramAnnotations = annotations[i];
4545
JsonProperty jsonProperty = getAnnotation(paramAnnotations, JsonProperty.class);
4646
if (jsonProperty == null) {
47-
throw new RuntimeException("must mark all parameters using @JsonProperty: " + ctor);
47+
throw new JsonException("must mark all parameters using @JsonProperty: " + ctor);
4848
}
4949
Binding binding = new Binding();
5050
binding.name = jsonProperty.value();
@@ -68,7 +68,7 @@ public void updateClassDescriptor(ClassDescriptor desc) {
6868
Annotation[] paramAnnotations = annotations[i];
6969
JsonProperty jsonProperty = getAnnotation(paramAnnotations, JsonProperty.class);
7070
if (jsonProperty == null) {
71-
throw new RuntimeException("must mark all parameters using @JsonProperty: " + method);
71+
throw new JsonException("must mark all parameters using @JsonProperty: " + method);
7272
}
7373
Binding binding = new Binding();
7474
binding.name = jsonProperty.value();
@@ -91,7 +91,7 @@ public void updateClassDescriptor(ClassDescriptor desc) {
9191
Annotation[] paramAnnotations = annotations[i];
9292
JsonProperty jsonProperty = getAnnotation(paramAnnotations, JsonProperty.class);
9393
if (jsonProperty == null) {
94-
throw new RuntimeException("must mark all parameters using @JsonProperty: " + method);
94+
throw new JsonException("must mark all parameters using @JsonProperty: " + method);
9595
}
9696
Binding binding = new Binding();
9797
binding.name = jsonProperty.value();

src/main/java/com/jsoniter/output/Codegen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.jsoniter.output;
22

3+
import com.jsoniter.JsonException;
34
import com.jsoniter.spi.Encoder;
45
import javassist.ClassPool;
56
import javassist.CtClass;
@@ -66,7 +67,7 @@ private static synchronized Encoder gen(String cacheKey, Type type) {
6667
} catch (Exception e) {
6768
System.err.println("failed to generate encoder for: " + type + " with " + Arrays.toString(typeArgs));
6869
System.err.println(source);
69-
throw new RuntimeException(e);
70+
throw new JsonException(e);
7071
}
7172
}
7273

0 commit comments

Comments
 (0)