2010-07-02 68 views
16

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

如果有一个Java字段没有相应的JSON,是否有简单的方法来抛出异常?也就是说,我希望要求JSON拥有Java结构中的所有字段。

回答

14

Gson没有JSON模式验证功能来指定特定元素必须存在,并且它没有指定必须填充Java成员的方法。有这样的功能可能会很好,例如@Required注释。前往the Gson Issues List并提出增强请求。

使用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<MyObject> 
{ 
    List<String> requiredFields = new ArrayList<String>(); 

    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 Schema验证的API。 Jackson has at least a rudimentary implementation availableJSON 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<String> 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); 
    } 
} 
+2

虽然在创建另一个GSON实例的类型你提出的GSON解决方案的工作,这是行不通的当重用相同的上下文时。它会导致无限循环。通过创建一个新的gson,你会失去原始gson所具有的其他配置选项。 – Moritz 2011-08-29 10:34:36

0

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

private static List<String> verifyElement(JsonObject element, Class klass) throws NoSuchFieldException, IllegalAccessException { 
    List<String> unknownFields = new ArrayList<>(); 
    Set<String> 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<String, JsonElement> 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<String> elementErrors = verifyElement(entry.getValue().getAsJsonObject(), fieldClass); 
     unknownFields.addAll(elementErrors); 
     } 
    } 
    } 
    return unknownFields; 

}