Gson:展平数组的冗余对象包装
Gson: Flatten redundant object wrap of an array
我有一种来自服务的数据
- 服务 A returns:
{
"name": "foo",
"id": 333,
"contact": [
{
"type": "phone",
"number": "12333333"
},
{
"type": "phone",
"number": "22333333"
}
]
}
- 我为它准备了模型class:
public class People {
public String name;
public int id;
public List<Contact> contact;
public static class Contact {
public String type;
public String number;
}
}
- 但是服务 B returns:
{
"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;
}
}
我有一种来自服务的数据
- 服务 A returns:
{
"name": "foo",
"id": 333,
"contact": [
{
"type": "phone",
"number": "12333333"
},
{
"type": "phone",
"number": "22333333"
}
]
}
- 我为它准备了模型class:
public class People {
public String name;
public int id;
public List<Contact> contact;
public static class Contact {
public String type;
public String number;
}
}
- 但是服务 B returns:
{
"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;
}
}