【问题标题】:Gson parsing error for my string-encoded inner JSON document我的字符串编码的内部 JSON 文档的 Gson 解析错误
【发布时间】:2017-05-12 05:27:07
【问题描述】:

我正在尝试使用 Gson 将来自服务器的响应 JSON 字符串解析为我的 POJO 对象。下面是我的 JSON:

{  
   "user":{  
      "id":"859adb60-4a47-4e1b-8f40-10480bfc33ec",
      "createdAt":"2017-05-11T07:52:43.661Z",
      "updatedAt":"2017-05-11T07:52:43.786Z",
      "version":"AAAAAAA0GXQ=",
      "deleted":false,
      "userName":null,
      "bucketId":"7c2b3ce4-ac5d-4e6a-a6c1-d6316c01fcfb",
      "userEmailId":"test1@tilicho.in",
      "signupType":"Custom",
      "userAvatar":null,
      "userPin":"$2a$10$9TCIqHrE6jDZMr0lhHRddeC7kU.Ob6s8o8zG3ahsmaAMBNi5Gkvwy",
      "verified":true,
      "verifyHash":"IpkzqvjHGb6nbdxxkvmgLiPJRSRhUJ6wMOO3V7Gz5ispOlpLPtdGMTkldzOwHp3Q",
      "thirdPartyToken":null,
      "loginAgent":null,
      "isDataSetUp":false
   },
   "colloboratedChildren":[  

   ],
   "invitations":[  
      {  
         "id":"4892167b-bc6a-47e5-bb30-1785b5643edc",
         "createdAt":"2017-05-11T07:52:06.019Z",
         "updatedAt":"2017-05-11T07:52:06.019Z",
         "version":"AAAAAAA0GWY=",
         "deleted":false,
         "inviterId":"9d00f972-e66d-400b-b332-f0b873a8b1fd",
         "inviterEmail":"nemani@tilicho.in",
         "inviteeId":null,
         "inviteeEmail":"test1@tilicho.in",
         "inviteData":"{\"821e63e7-457e-401f-b071-abacd51bcbad\":[\"B690E26C-BA39-45E3-BB34-CF6EABB666F7\"]}"
      }
   ],
   "authToken":"bQYng6AAeCp9HlR1rCZeOCbFUm1LaRfVzcqTWpgXiLul3tFj7Y4hKr997V6hvl6d"
}

这是我的 POJO 课程。

@Getter
@Setter
public class LoginResponse {

    User user;
    ArrayList<ColloboratedChildren> colloboratedChildren;
    ArrayList<InvitationResponse> invitations;
    String authToken;

}

@Getter
@Setter
public class User extends BucketAzureModel {

    private String signupType;
    private String userAvatar;
    private String userEmailId;
    private String userName;
    private String userPin;


}

@Getter
@Setter
ColloboratedChildren {}

public class InvitationResponse {

    String id;
    String createdAt;
    String updatedAt;
    String version;
    boolean deleted;
    String inviterId;
    String inviterEmail;
    String inviteeId;
    String inviteeEmail;
    Map<String, ArrayList<String>> inviteData;

}

将 JSON 转换为 POJO 的代码:

JsonEntityParser.parseResults(jsonElement, gsonBuilder.create(), concreteClass);

这是我面临的错误:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at path $.invitations[0].inviteData
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220)
at com.google.gson.Gson.fromJson(Gson.java:887)
at com.google.gson.Gson.fromJson(Gson.java:952)
at com.google.gson.Gson.fromJson(Gson.java:925)
at com.microsoft.windowsazure.mobileservices.table.serialization.JsonEntityParser.parseResults(JsonEntityParser.java:64)

从错误中,我了解到 Gson 期望 { 有一个 ",但我不完全了解如何根据给定的响应在 inviteData 创建一个 Map&lt;String, ArrayList&lt;String&gt;&gt;。我真的不知道如何定义我的 POJO 以解析传入的 JSON。请注意,我无法更改 JSON 的格式,我只能遵守这种格式。任何帮助将不胜感激。

【问题讨论】:

标签: java json parsing gson


【解决方案1】:

无论出于何种原因设计了您要处理的响应,Gson 都可以使其工作:Gson 支持@JsonAdapter,允许使用临时(反)序列化。

你好像用Lombok,但是只要我不使用Lombok,而Gson在(反)序列化时默认使用字段,你可以注解inviteData字段:

final class LoginResponse {

    final List<InvitationResponse> invitations = null;

}
final class InvitationResponse {

    @JsonAdapter(FromString.class)
    final Map<String, List<String>> inviteData = null;

}

FromString 类型的适配器工厂非常简单:

final class FromString
        implements TypeAdapterFactory {

    // Gson can instantiate it itself   
    private FromString() {
    }

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        // Obtaining the original type adapter for the declared field
        return new FromStringTypeAdapter<>(gson.getAdapter(typeToken)).nullSafe();
    }

    private static final class FromStringTypeAdapter<T>
            extends TypeAdapter<T> {

        private final TypeAdapter<T> typeAdapter;

        private FromStringTypeAdapter(final TypeAdapter<T> typeAdapter) {
            this.typeAdapter = typeAdapter;
        }

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

        @Override
        public T read(final JsonReader in)
                throws IOException {
            // Just read the current value as a string, and delegate the job to the obtained type adapter
            final String innerJson = in.nextString();
            return typeAdapter.fromJson(innerJson);
        }

    }

}

就是这样。示例:

try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43929916.class, "loginResponse.json") ) {
    final LoginResponse loginResponse = gson.fromJson(jsonReader, LoginResponse.class);
    for ( final InvitationResponse invitation : loginResponse.invitations ) {
        System.out.println(invitation.inviteData);
    }
}

输出:

{821e63e7-457e-401f-b071-abacd51bcbad=[B690E26C-BA39-45E3-BB34-CF6EABB666F7]}

请注意,此解决方案对于不大的内部 JSON 足够有效,因为 Gson 目前只允许从 JSON 令牌输入中读取整个字符串(因此需要内存缓冲,即使这样的缓冲也可能消耗太多)。为了处理更大的内部 JSON,您可能需要投票给 https://github.com/google/gson/issues/971,这将允许返回字符串读取器,因此不需要中间缓冲区。

【讨论】:

    【解决方案2】:

    com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 应为 BEGIN_OBJECT,但路径为 STRING $.invitations[0].inviteData

    因此,此错误表明您期望 inviteData 的类型为 Map&lt;String,ArrayList&lt;String&gt;&gt;,但返回的不是 Map,而是 String

    "inviteData":"{\"821e63e7-457e-401f-b071-abacd51bcbad\":[\"B690E26C-BA39-45E3-BB34-CF6EABB666F7\"]}"
                ||
                ||
                ||
                --->This is a String not of type Map<String,ArrayList<String>>
    

    所以你必须在你的 POJO 中做适当的改变,

    class InvitationResponse {
    
        String id;
        String createdAt;
        String updatedAt;
        String version;
        boolean deleted;
        String inviterId;
        String inviterEmail;
        String inviteeId;
        String inviteeEmail;
        String inviteData; ----> This should be String
    }
    

    如果您的 JSON 如下所示:

    "inviteData" : {
                    "someKey" : [ "SomeValue1","SomeValue2" ]
                 }
    

    那么你的解决方案就会奏效。

    现在,由于这个字符串对你没有用,所以如果你是那个 JSON 的提供者,你要么必须更改 JSON 响应,否则你必须付出额外的努力,再次将此字符串值更改为 Map&lt;String,ArrayList&lt;String&gt;&gt;

    【讨论】:

    • 谢谢,@Neeraj Jain 忽略了“。无法更改响应,将不得不付出额外的努力。
    • 不用担心,我经常遇到这种情况:),只需使用相同的 gson 对象即可。
    • 有一个解决方案允许使用映射而不是字符串,因此不需要额外的字符串反序列化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    相关资源
    最近更新 更多