使用Google的Gson进行严格的JSON解析?

假设我使用Google的Gson库将JSON解析为Java数据结构。

如果存在没有相应JSON的Java字段,是否有一种简单的方法可以抛出exception? 也就是说,我希望JSON拥有Java结构中的所有字段。

Gson没有JSON模式validationfunction来指定必须存在特定元素,并且它没有办法指定必须填充Java成员。 拥有这样的function可能会很好,例如使用@Required注释。 前往Gson问题列表并提出增强请求。

使用Gson,您可以强制使用自定义反序列化程序存在指定的JSON元素。

 // output: // [MyObject: element1=value1, element2=value2, element3=value3] // [MyObject: element1=value1, element2=value2, element3=null] // Exception in thread "main" com.google.gson.JsonParseException: Required Field Not Found: element2 import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; public class Foo { static String jsonInput1 = "{\"element1\":\"value1\",\"element2\":\"value2\",\"element3\":\"value3\"}"; static String jsonInput2 = "{\"element1\":\"value1\",\"element2\":\"value2\"}"; static String jsonInput3 = "{\"element1\":\"value1\",\"element3\":\"value3\"}"; public static void main(String[] args) { GsonBuilder gsonBuilder = new GsonBuilder(); MyDeserializer deserializer = new MyDeserializer(); deserializer.registerRequiredField("element2"); gsonBuilder.registerTypeAdapter(MyObject.class, deserializer); Gson gson = gsonBuilder.create(); MyObject object1 = gson.fromJson(jsonInput1, MyObject.class); System.out.println(object1); MyObject object2 = gson.fromJson(jsonInput2, MyObject.class); System.out.println(object2); MyObject object3 = gson.fromJson(jsonInput3, MyObject.class); System.out.println(object3); } } class MyObject { String element1; String element2; String element3; @Override public String toString() { return String.format( "[MyObject: element1=%s, element2=%s, element3=%s]", element1, element2, element3); } } class MyDeserializer implements JsonDeserializer { List requiredFields = new ArrayList(); void registerRequiredField(String fieldName) { requiredFields.add(fieldName); } @Override public MyObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = (JsonObject) json; for (String fieldName : requiredFields) { if (jsonObject.get(fieldName) == null) { throw new JsonParseException("Required Field Not Found: " + fieldName); } } return new Gson().fromJson(json, MyObject.class); } } 

一种更好的方法可能是使用提供JSON Schemavalidation的API。 jackson至少有一个基本的实施可用 。 JSON Tools看起来更成熟。

这是jackson的一个例子。

 // output: // Validating jsonInput1... // Validating jsonInput2... // Validating jsonInput3... // $.element2: is missing and it is not optional // [MyObject: element1=value1, element2=value2, element3=value3] // [MyObject: element1=value1, element2=value2, element3=null] // [MyObject: element1=value1, element2=null, element3=value3] import java.util.List; import org.codehaus.jackson.map.ObjectMapper; import eu.vahlas.json.schema.JSONSchema; import eu.vahlas.json.schema.JSONSchemaProvider; import eu.vahlas.json.schema.impl.JacksonSchemaProvider; public class Foo { static String jsonSchema = "{" + "\"description\":\"Serialized MyObject Specification\"," + "\"type\":[\"object\"]," + "\"properties\":" + "{" + "\"element1\":{\"type\":\"string\"}," + "\"element2\":{\"type\":\"string\",\"optional\":false}," + "\"element3\":{\"type\":\"string\",\"optional\":true}" + "}" + "}";; static String jsonInput1 = "{\"element1\":\"value1\",\"element2\":\"value2\",\"element3\":\"value3\"}"; static String jsonInput2 = "{\"element1\":\"value1\",\"element2\":\"value2\"}"; static String jsonInput3 = "{\"element1\":\"value1\",\"element3\":\"value3\"}"; public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); JSONSchemaProvider schemaProvider = new JacksonSchemaProvider(mapper); JSONSchema schema = schemaProvider.getSchema(jsonSchema); System.out.println("Validating jsonInput1..."); validateAndLogErrors(jsonInput1, schema); System.out.println("Validating jsonInput2..."); validateAndLogErrors(jsonInput2, schema); System.out.println("Validating jsonInput3..."); validateAndLogErrors(jsonInput3, schema); MyObject object1 = mapper.readValue(jsonInput1, MyObject.class); System.out.println(object1); MyObject object2 = mapper.readValue(jsonInput2, MyObject.class); System.out.println(object2); MyObject object3 = mapper.readValue(jsonInput3, MyObject.class); System.out.println(object3); } static void validateAndLogErrors(String jsonInput, JSONSchema schema) { List errors = schema.validate(jsonInput); for (String error : errors) { System.out.println(error); } } } class MyObject { String element1; String element2; String element3; void setElement1(String element1) { this.element1 = element1; } void setElement2(String element2) { this.element2 = element2; } void setElement3(String element3) { this.element3 = element3; } @Override public String toString() { return String.format( "[MyObject: element1=%s, element2=%s, element3=%s]", element1, element2, element3); } } 

您可以递归validationjson是否包含未在类中声明的字段:

  private static List verifyElement(JsonObject element, Class klass) throws NoSuchFieldException, IllegalAccessException { List unknownFields = new ArrayList<>(); Set classFields = new HashSet<>(); for (Field field : klass.getDeclaredFields()) { if (!Modifier.isPublic(field.getModifiers())) { throw new IllegalArgumentException("All fields must be public. Please correct this field :" + field); } } for (Field field : klass.getFields()) { classFields.add(field.getName()); } // Verify recursively that the class contains every for (Map.Entry entry : element.entrySet()) { if (!classFields.contains(entry.getKey())) { unknownFields.add(klass.getCanonicalName() + "::" + entry.getKey() + "\n"); } else { Field field = klass.getField(entry.getKey()); Class fieldClass = field.getType(); if (!fieldClass.isPrimitive() && entry.getValue().isJsonObject()) { List elementErrors = verifyElement(entry.getValue().getAsJsonObject(), fieldClass); unknownFields.addAll(elementErrors); } } } return unknownFields; }