Gson:无法将JsonArray转换为JsonPrimitive错误

时间:2019-03-22 18:04:37

标签: java json gson

我想使用Gson解析包含动态字段类型的json对象:

{
"rows":
   [
      {
         "id": "1",
         "interventions": [
            {
               "type": "type1",
               "label": "label 1"
            },
            {
               "type": "type2",
               "label": ["label 1","label 2"]
           },
           {
              "type": "type3",
              "label": "label 3",
           }
        ]
     }
  ]

}

您会看到“标签”字段可以是String或字符串列表。

我写了一个定制的反序列化程序来处理此问题,如果“干预”字段只有一个元素(不管“标签”字段是字符串还是列表),它都可以工作:

{"rows":
  [
     {
        "id": "1",
        "interventions": [
           {
              "type": "type1",
              "label": "label 1"
           }
        ]
     }
  ]

}

但总是抛出com.google.gson.JsonArray,如果有多个“干预”元素,则不能将其强制转换为com.google.gson.JsonPrimitive异常。

这是自定义的反序列化器:

public class CustomDeserializer implements JsonDeserializer<InterventionsModel> {

@Override
public InterventionsModel deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {

    if(je != null && je.getAsJsonObject()!=null) {

        JsonPrimitive jp = je.getAsJsonObject().getAsJsonPrimitive("label");
        if (jp != null && jp.isString()) {

            String label = jp.getAsString();            
            List<String> list = new ArrayList<String>(1);
            list.add(label);

            InterventionsModel interventionsModel = new InterventionsModel();
            interventionsModel.setLabel(list);

            return interventionsModel;            
        }
    }

    return new Gson().fromJson(je, InterventionsModel.class);
}

}

在调用方法中:

GsonBuilder builder = new GsonBuilder(); 
    builder.registerTypeAdapter(InterventionsModel.class, new CustomDeserializer());
    builder.setPrettyPrinting(); 
    Gson gson = builder.create();

对象的类为:

public class ResultsModel {
private List<RowModel> rows;

//getter and setter ..    

}

public class RowModel {
private String id;    
private List<InterventionsModel> interventions;
//getter and setter

}

public class InterventionsModel {
private String type;
private List<String> label;
//setter and getter

}

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:3)

您不必为整个InterventionsModel创建自定义反序列化器。
相反,只需将@JsonAdapter注释应用于List<String> label字段

public class InterventionsModel {
    private String type;

    @JsonAdapter(LabelsDeserializer.class)
    private List<String> label;

    // Setters and getters
}

并为List<String>类型创建反序列化器

public class LabelsDeserializer implements JsonDeserializer<List<String>> {
    @Override
    public List<String> deserialize(
            final JsonElement json,
            final Type typeOfT,
            final JsonDeserializationContext context) {
        // Check if the JSON object is an array or a primitive value
        if (json.isJsonArray()) {
            // Multiple Strings elements
            final JsonArray jsonArray = json.getAsJsonArray();
            final List<String> labels = new ArrayList<>(jsonArray.size());

            for (final JsonElement jsonElement : jsonArray) {
                labels.add(jsonElement.getAsString());
            }

            return labels;
        }

        // Single String element
        return Collections.singletonList(json.getAsString());
    }
}

Java模型的字段type与JSON文档字段intervention_type之间也存在不匹配。

作为一般建议,请尝试始终自定义代码的最短/最小部分,并尝试构建通用代码。自定义会带来很多工作,随着时间的流逝需要维护。


对于Gson 2.6.*,使用

public class LabelsDeserializer extends TypeAdapter<List<String>> {
    @Override
    public void write(
            final JsonWriter out,
            final List<String> labels) throws IOException {
        if (labels.size() == 1) {
            out.value(labels.get(0));
            return;
        }

        out.beginArray();

        for (final String l : labels) {
            out.value(l);
        }

        out.endArray();
    }

    @Override
    public List<String> read(final JsonReader in) throws IOException {
        final JsonToken peek = in.peek();

        if (peek.equals(JsonToken.BEGIN_ARRAY)) {
            final List<String> labels = new ArrayList<>();
            in.beginArray();

            while (in.hasNext()) {
                labels.add(in.nextString());
            }

            in.endArray();
            return labels;
        }

        return Collections.singletonList(in.nextString());
    }
}