Gson:展平数组的冗余对象包装

Gson: Flatten redundant object wrap of an array

我有一种来自服务的数据

{
  "name": "foo",
  "id": 333,
  "contact": [
    {
      "type": "phone",
      "number": "12333333"
    },
    {
      "type": "phone",
      "number": "22333333"
    }
  ]
}
public class People {
    public String name;
    public int id;
    public List<Contact> contact;

    public static class Contact {
        public String type;
        public String number;
    }
    
}

{
  "name": "foo",
  "id": 333,
  "contact":{
    "entries": [
      {
        "type": "phone",
        "number": "12333333"
      },
      {
        "type": "phone",
        "number": "22333333"
      }
    ]
  }
}

实际情况是json中有10个列表,这个JSON中的所有列表都被一个对象包裹起来,那么“entries”就是实际的列表。 我已经在其他地方使用了模式 class,我只想将它们视为相同的 class,例如:

Contact contact = people.contact

有什么想法吗?

您可以在值可能被包装的字段上使用自定义 TypeAdapterFactory and a @JsonAdapter 注释来解决此问题。

TypeAdapterFactory 用于获取委托适配器(例如 List<Contact>),然后在必要时创建一个展开 JSON 的适配器。 @JsonAdapter 注释用于将此工厂仅应用于受影响的字段,而不是一般地应用于所有 List 值。

/**
 * Gson {@link TypeAdapterFactory} which unwraps lists inside JSON objects, if
 * necessary.
 * 
 * <p>Must <b>only</b> be used with {@link JsonAdapter @JsonAdapter}.
 */
public static class WrappedListTypeAdapterFactory implements TypeAdapterFactory {
    // Default constructor to allow Gson to create instances
    public WrappedListTypeAdapterFactory() { }

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // Get the actual adapter, for example for `List<Contact>`
        // Note: Cannot use Gson.getDelegateAdapter, see https://github.com/google/gson/issues/1028
        TypeAdapter<T> delegate = gson.getAdapter(type);

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegate.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                JsonToken peeked = in.peek();
                if (peeked == JsonToken.NULL) {
                    return null;
                } else if (peeked == JsonToken.BEGIN_ARRAY) {
                    // JSON array is not wrapped, read it directly
                    return delegate.read(in);
                }

                // Start unwrapping the JSON array
                in.beginObject();
                String propertyName = in.nextName();
                if (!propertyName.equals("entries")) {
                    // Don't include property name to avoid log injection attacks
                    throw new JsonParseException("Unexpected property name");
                }

                // Now read the JSON array
                T value = delegate.read(in);
                in.endObject();

                return value;
            }
        };
    }
}
public class People {
    public String name;
    public int id;

    @JsonAdapter(WrappedListTypeAdapterFactory.class)
    public List<Contact> contact;

    public static class Contact {
        public String type;
        public String number;
    }
}