如何使用 Gson 序列化对对象的响应?
How to serialize the response to object by using Gson?
我直接向 VK api
提出请求 token
像这样:https://api.vk.com/method/groups.get?fields=photo_50&access_token=MY_TOKEN&filter=admin%2C%20editor%2C%20moder&extended=1
这里是 spec about api
但是我无法使用 Gson
序列化对对象的响应,因为响应数组有 int
值:
{
"response": [
2,
{
"gid": 59295,
"name": "Создание",
"screen_name": "book",
"is_closed": 0,
"type": "group",
"photo_50": "https://pp.userapi.com/qwvD6SPkYzo.jpg"
},
{
"gid": 57150,
"name": "Массаж",
"screen_name": "club10450",
"is_closed": 2,
"type": "group",
"photo_50": "https://pp.userapi.com/ZKnmRkS1izs.jpg"
}
]
}
如何使用 Gson
将其序列化为对象?
已解决,已将参数添加到 url v=5.61
version number
{
"response": {
"count": 190,
"items": [{
"id": 28261334,
"name": "TJ",
"screen_name": "tj",
"is_closed": 0,
"type": "page",
"is_admin": 0,
"is_member": 1,
"photo_50": "https://pp.vk.me/...f2c/06crfCSL1KY.jpg"
}]
}
}
尽管您已经通过 GET URL 参数更改 API 版本解决了问题,但这里有一个处理 "non-standard" JSONs 的方法将来可能会遇到。我假设您有正确的映射,但数组长度(大概)被放置为第一个数组元素。 Gson 本身无法处理这种特殊情况(至少如果它需要 {...}
个对象),可能会给你这样的东西:
Expected BEGIN_OBJECT but was NUMBER at line 3 column 10 path $.response[0]
假设您有类似于下两个的映射:
final class ElementsResponse {
@SerializedName("response")
final List<Element> response = null;
}
final class Element {
@SerializedName("gid")
final int gid = Integer.valueOf(0);
@SerializedName("name")
final String name = null;
@SerializedName("screen_name")
final String screenName = null;
@SerializedName("is_closed")
final int isClosed = Integer.valueOf(0);
@SerializedName("type")
final String type = "";
@SerializedName("photo_50")
final URL photo50 = null;
}
您可以使用特殊的类型适配器工厂轻松创建您的类型适配器,以处理给定的 JSON:
final class LengthArrayTypeAdapterFactory
implements TypeAdapterFactory {
// The instance holds no state and can be created as a singleton
private static final TypeAdapterFactory lengthArrayTypeAdapterFactory = new LengthArrayTypeAdapterFactory();
private LengthArrayTypeAdapterFactory() {
}
// However, the factory method does not let a caller to create an instance itself, and _may_ create it itself if necessary (encapsulation)
static TypeAdapterFactory getLengthArrayTypeAdapterFactory() {
return lengthArrayTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Are we dealing with a java.util.List instance?
if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
// Resolve the list element type if possible
final Type elementType = getElementType(typeToken.getType());
// And request Gson for the element type adapter
final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
// Some Java boilerplate regarding generics in order not letting the @SuppressWarnings annotation cover too much
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LengthArrayTypeAdapter<>(elementTypeAdapter);
return castTypeAdapter;
}
// Or let Gson pick the next downstream type adapter itself
return null;
}
private static Type getElementType(final Type listType) {
// The given type is not parameterized?
if ( !(listType instanceof ParameterizedType) ) {
// Probably the (de)serialized list is raw being not parameterized
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) listType;
// Or just take the first type parameter (java.util.List has one type parameter only)
return parameterizedType.getActualTypeArguments()[0];
}
private static final class LengthArrayTypeAdapter<E>
extends TypeAdapter<List<E>> {
// This type adapter is designed to read and write a single element only
// We'll take care of all elements array ourselves
private final TypeAdapter<E> elementTypeAdapter;
private LengthArrayTypeAdapter(final TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
@Override
public List<E> read(final JsonReader in)
throws IOException {
// Gson type adapters are supposed to be null-friendly
if ( in.peek() == NULL ) {
return null;
}
// Consume the array begin token `[`
in.beginArray();
// The next value is most likely the array length?
final int arrayLength = in.nextInt();
final List<E> list = new ArrayList<>();
// Read until the array has more elements
while ( in.hasNext() ) {
// And let the element type adapter read the array element so push the value to the list
list.add(elementTypeAdapter.read(in));
}
// Consume the array end token `]`
in.endArray();
assert arrayLength == list.size();
return list;
}
@Override
@SuppressWarnings("resource")
public void write(final JsonWriter out, final List<E> list)
throws IOException {
if ( list == null ) {
// Must be null-friendly always
out.nullValue();
} else {
// Writing the `[` token
out.beginArray();
// Writing the list size/length
out.value(list.size());
for ( final E element : list ) {
// And just write each array element
elementTypeAdapter.write(out, element);
}
// Finalizing the writing with `]`
out.endArray();
}
}
}
}
所以您所要做的就是将类型适配器工厂添加到 Gson
配置中,创建您的特殊数组感知 Gson
:
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getLengthArrayTypeAdapterFactory())
.create();
final ElementsResponse elementsResponse = gson.fromJson(JSON, ElementsResponse.class);
elementsResponse.response.forEach(e -> System.out.println(e.name));
System.out.println(gson.toJson(elementsResponse));
输出:
Создание
Массаж
{"response":[2,{"gid":59295,"name":"Создание","screen_name":"book","is_closed":0,"type":"group","photo_50":"https://pp.userapi.com/qwvD6SPkYzo.jpg"},{"gid":57150,"name":"Массаж","screen_name":"club10450","is_closed":2,"type":"group","photo_50":"https://pp.userapi.com/ZKnmRkS1izs.jpg"}]}
请注意,此类型适配器工厂始终假定第一个数组元素是一个数字,如果需要,您可能需要分析 elementType
(例如,如果它是 java.lang.Number
或其子类)。
我直接向 VK api
提出请求 token
像这样:https://api.vk.com/method/groups.get?fields=photo_50&access_token=MY_TOKEN&filter=admin%2C%20editor%2C%20moder&extended=1
这里是 spec about api
但是我无法使用 Gson
序列化对对象的响应,因为响应数组有 int
值:
{
"response": [
2,
{
"gid": 59295,
"name": "Создание",
"screen_name": "book",
"is_closed": 0,
"type": "group",
"photo_50": "https://pp.userapi.com/qwvD6SPkYzo.jpg"
},
{
"gid": 57150,
"name": "Массаж",
"screen_name": "club10450",
"is_closed": 2,
"type": "group",
"photo_50": "https://pp.userapi.com/ZKnmRkS1izs.jpg"
}
]
}
如何使用 Gson
将其序列化为对象?
已解决,已将参数添加到 url v=5.61
version number
{
"response": {
"count": 190,
"items": [{
"id": 28261334,
"name": "TJ",
"screen_name": "tj",
"is_closed": 0,
"type": "page",
"is_admin": 0,
"is_member": 1,
"photo_50": "https://pp.vk.me/...f2c/06crfCSL1KY.jpg"
}]
}
}
尽管您已经通过 GET URL 参数更改 API 版本解决了问题,但这里有一个处理 "non-standard" JSONs 的方法将来可能会遇到。我假设您有正确的映射,但数组长度(大概)被放置为第一个数组元素。 Gson 本身无法处理这种特殊情况(至少如果它需要 {...}
个对象),可能会给你这样的东西:
Expected BEGIN_OBJECT but was NUMBER at line 3 column 10 path $.response[0]
假设您有类似于下两个的映射:
final class ElementsResponse {
@SerializedName("response")
final List<Element> response = null;
}
final class Element {
@SerializedName("gid")
final int gid = Integer.valueOf(0);
@SerializedName("name")
final String name = null;
@SerializedName("screen_name")
final String screenName = null;
@SerializedName("is_closed")
final int isClosed = Integer.valueOf(0);
@SerializedName("type")
final String type = "";
@SerializedName("photo_50")
final URL photo50 = null;
}
您可以使用特殊的类型适配器工厂轻松创建您的类型适配器,以处理给定的 JSON:
final class LengthArrayTypeAdapterFactory
implements TypeAdapterFactory {
// The instance holds no state and can be created as a singleton
private static final TypeAdapterFactory lengthArrayTypeAdapterFactory = new LengthArrayTypeAdapterFactory();
private LengthArrayTypeAdapterFactory() {
}
// However, the factory method does not let a caller to create an instance itself, and _may_ create it itself if necessary (encapsulation)
static TypeAdapterFactory getLengthArrayTypeAdapterFactory() {
return lengthArrayTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Are we dealing with a java.util.List instance?
if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
// Resolve the list element type if possible
final Type elementType = getElementType(typeToken.getType());
// And request Gson for the element type adapter
final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
// Some Java boilerplate regarding generics in order not letting the @SuppressWarnings annotation cover too much
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) new LengthArrayTypeAdapter<>(elementTypeAdapter);
return castTypeAdapter;
}
// Or let Gson pick the next downstream type adapter itself
return null;
}
private static Type getElementType(final Type listType) {
// The given type is not parameterized?
if ( !(listType instanceof ParameterizedType) ) {
// Probably the (de)serialized list is raw being not parameterized
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) listType;
// Or just take the first type parameter (java.util.List has one type parameter only)
return parameterizedType.getActualTypeArguments()[0];
}
private static final class LengthArrayTypeAdapter<E>
extends TypeAdapter<List<E>> {
// This type adapter is designed to read and write a single element only
// We'll take care of all elements array ourselves
private final TypeAdapter<E> elementTypeAdapter;
private LengthArrayTypeAdapter(final TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
@Override
public List<E> read(final JsonReader in)
throws IOException {
// Gson type adapters are supposed to be null-friendly
if ( in.peek() == NULL ) {
return null;
}
// Consume the array begin token `[`
in.beginArray();
// The next value is most likely the array length?
final int arrayLength = in.nextInt();
final List<E> list = new ArrayList<>();
// Read until the array has more elements
while ( in.hasNext() ) {
// And let the element type adapter read the array element so push the value to the list
list.add(elementTypeAdapter.read(in));
}
// Consume the array end token `]`
in.endArray();
assert arrayLength == list.size();
return list;
}
@Override
@SuppressWarnings("resource")
public void write(final JsonWriter out, final List<E> list)
throws IOException {
if ( list == null ) {
// Must be null-friendly always
out.nullValue();
} else {
// Writing the `[` token
out.beginArray();
// Writing the list size/length
out.value(list.size());
for ( final E element : list ) {
// And just write each array element
elementTypeAdapter.write(out, element);
}
// Finalizing the writing with `]`
out.endArray();
}
}
}
}
所以您所要做的就是将类型适配器工厂添加到 Gson
配置中,创建您的特殊数组感知 Gson
:
final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(getLengthArrayTypeAdapterFactory())
.create();
final ElementsResponse elementsResponse = gson.fromJson(JSON, ElementsResponse.class);
elementsResponse.response.forEach(e -> System.out.println(e.name));
System.out.println(gson.toJson(elementsResponse));
输出:
Создание
Массаж
{"response":[2,{"gid":59295,"name":"Создание","screen_name":"book","is_closed":0,"type":"group","photo_50":"https://pp.userapi.com/qwvD6SPkYzo.jpg"},{"gid":57150,"name":"Массаж","screen_name":"club10450","is_closed":2,"type":"group","photo_50":"https://pp.userapi.com/ZKnmRkS1izs.jpg"}]}
请注意,此类型适配器工厂始终假定第一个数组元素是一个数字,如果需要,您可能需要分析 elementType
(例如,如果它是 java.lang.Number
或其子类)。