一尘不染

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

java

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

如果Java字段没有对应的JSON,是否有一种简单的引发异常的方法?也就是说,我希望要求JSON具有Java结构中的所有字段。


阅读 257

收藏
2020-12-03

共1个答案

一尘不染

Gson没有JSON模式验证功能来指定必须存在一个特定的元素,也没有办法指定必须填充Java成员。拥有这样的功能(例如带有@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<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模式验证的API 。
杰克逊至少有一个基本的实施方案
JSON工具似乎已经成熟了。

这是杰克逊的一个例子。

// 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);
  }
}
2020-12-03