假設我使用Google的Gson庫將JSON解析爲Java數據結構。使用Google的Gson嚴格分析JSON?
如果有一個Java字段沒有相應的JSON,是否有簡單的方法來拋出異常?也就是說,我希望要求JSON擁有Java結構中的所有字段。
假設我使用Google的Gson庫將JSON解析爲Java數據結構。使用Google的Gson嚴格分析JSON?
如果有一個Java字段沒有相應的JSON,是否有簡單的方法來拋出異常?也就是說,我希望要求JSON擁有Java結構中的所有字段。
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 available。 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<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);
}
}
您可以遞歸驗證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;
}
雖然在創建另一個GSON實例的類型你提出的GSON解決方案的工作,這是行不通的當重用相同的上下文時。它會導致無限循環。通過創建一個新的gson,你會失去原始gson所具有的其他配置選項。 – Moritz 2011-08-29 10:34:36