gson反序列化双重嵌套字段

rbpvctlc  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(142)

下面是json:

{
"name": "Nutrition Information",
  "id": "12345",
  "nutrition": {
    "nutrients": [
      {
        "name": "Calories",
        "amount": 3958.2,
        "unit": "kcal",
        "percentOfDailyNeeds": 197.91
      }
      // More Nutrient entries...
    ],
    "properties": [
      // some property entries...
    ]
  }
}

字符串
现在我想把“name”、“id”和所有“nutrition”条目放到一个带有Gson的类中。我的类看起来像这样:

public class Recipe {
    private int id;
    private String name;
    
    @SerializedName("nutrients")
    private ArrayList<Nutrient> nutrientList = new ArrayList<>();

    public Recipe() {}

    //Getter and Setter


    //Inner Nutrition class 
    public static class Nutrient {
        @SerializedName("name")
        private String name;
        @SerializedName("amount")
        private float amount;
        @SerializedName("unit")
        private String unit;

    //Getter and Setter
    }

}


实际上,json和我的类都有一些更多的字段,如“id”和“name”,但为了清楚起见,我排除了它们。我的问题是我不能“跳过”“营养”到“营养素”。我尝试创建一个内部类“Nutrition”,它包含内部类“Nutrient”,它工作了,但我对这个解决方案不满意。“营养”课是不必要的。
我也尝试了自定义反序列化,但从我发现它只可能定义整个反序列化?我想保持“id”和“name”以及我排除的其他字段不变,并让它们自动反序列化。

ajsxfq5m

ajsxfq5m1#

如果只想上移一个字段,则可以使用自定义的TypeAdapterFactory来解决这个问题,该自定义的TypeAdapterFactory所创建的适配器将跳过该字段并委托嵌套对象的反序列化。您可以使用@JsonAdapter annotation仅将此特殊的反序列化应用于Recipe.nutrientList字段,而不是将其应用于所有List<Nutrient>字段。
举例来说:

class UnwrappingNutrientsAdapterFactory implements TypeAdapterFactory {
    // Constructor is called by Gson using reflection
    public UnwrappingNutrientsAdapterFactory() {
    }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // Note: Should rather use Gson.getDelegateAdapter(...), but that currently does not work
        // in combination with @JsonAdapter, see https://github.com/google/gson/issues/1028
        TypeAdapter<T> delegate = gson.getAdapter(type);

        return new TypeAdapter<T>() {
            @Override
            public T read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }

                T nutrients = null;

                // TODO: You could enhance the code below to detect when the "nutrients"
                //  property is missing or appears multiple times
                in.beginObject();
                while (in.hasNext()) {
                    String name = in.nextName();

                    if (name.equals("nutrients")) {
                        nutrients = delegate.read(in);
                    } else {
                        // Skip all other properties
                        in.skipValue();
                    }
                }
                in.endObject();

                return nutrients;
            }

            @Override
            public void write(JsonWriter out, T value) throws IOException {
                throw new UnsupportedOperationException();
            }
        };
    }
}

字符串
然后你像这样应用这个工厂:

class Recipe {
    ...

    @JsonAdapter(UnwrappingNutrientsAdapterFactory.class)
    @SerializedName("nutrition")
    private ArrayList<Nutrient> nutrientList = new ArrayList<>();

    ...
}


在这里,您必须使用"nutrition"作为@SerializedName的值,因为这是JSON数据中出现的名称,并且其值是未 Package 的。这种方法只适用于反序列化,而不适用于序列化。
但是,如果您必须上移多个字段,则会变得更加复杂,并且无法再直接使用上面显示的方法。有关此问题,请参见How to implement a Gson equivalent of @JsonUnwrapGSON: How to move fields to parent object

相关问题