【问题标题】:Gson - deserialising different typesGson - 反序列化不同类型
【发布时间】:2020-08-20 09:15:14
【问题描述】:

我正在处理传入的 JSON 字符串,并希望使用 GSON 将它们反序列化为类型化的 POJO 对象。 然而,推送字符串的服务器可以发送不同类型的对象 - 尽管类型是在 JSON 有效负载中定义的。

所以看看下面两个 JSON 字符串,其中我有一个 tradeEvent 和一个 errorEvent 对象(还有 5 种其他类型,如结算事件、支付事件等)。

我如何将其反序列化为 GSON 中的实际 POJO(可能使用泛型),因为直到运行时我才知道类型 - 正如您所见,第二级元素包含实际的对象类型(tradeEvent、errorEvent 等)。

还应该添加 - 就 POJO 而言,我是将第二个元素(即 tradeEvent、errorEvent)表示为对象还是字符串?

 {
  "data": {
    "tradeEvent": {
      "tradeId": "2d28d464-a746-4c58-b19f-b586d2f5d015",
      "status": 2,
      "eventDescription": "Trade Settled"
    }
  }
}


{
  "data": {
    "errorEvent": {
      "Uuid": "3a36ae26-ba41-40d5-b11d-d8d842eb2356",
    "failureCode": 2, "tradeId": "2d28d464-a746-4c58-b19f-b586d2f5d015", "errorMessage": "Returned error: Exception while processing transaction: trade not matched"
    }
  }
}

感谢您的指导。

【问题讨论】:

    标签: java json gson


    【解决方案1】:

    实现一个数据包装类来提取事件对象可能是最简单的方法:

    final class WrapperDto {
    
        @Nullable
        @SerializedName("data")
        @Expose(serialize = false, deserialize = true)
        private final DataDto data;
    
        @SuppressWarnings("unused")
        private WrapperDto(@Nullable final DataDto data) {
            this.data = data;
        }
    
        @Nullable
        <E extends Event> E toEvent() {
            if ( data == null ) {
                return null;
            }
            return data.toEvent();
        }
    
        private static final class DataDto {
    
            @Nullable
            @SerializedName("tradeEvent")
            @Expose(serialize = false, deserialize = true)
            private final Event.Trade tradeEvent;
    
            @Nullable
            @SerializedName("errorEvent")
            @Expose(serialize = false, deserialize = true)
            private final Event.Error errorEvent;
    
            @SuppressWarnings("unused")
            private DataDto(@Nullable final Event.Trade tradeEvent, @Nullable final Event.Error errorEvent) {
                this.tradeEvent = tradeEvent;
                this.errorEvent = errorEvent;
            }
    
            @Nullable
            private <E extends Event> E toEvent()
                    throws IllegalStateException {
                @Nullable
                Event bestEvent = null;
                for ( final Event event : new Event[]{ tradeEvent, errorEvent } ) {
                    if ( bestEvent == null ) {
                        bestEvent = event;
                    } else if ( event != null ) {
                        throw new IllegalStateException("Ambiguity detected. event=" + event.getClass().getSimpleName() + ", bestEvent=" + bestEvent.getClass().getSimpleName());
                    }
                }
                @SuppressWarnings("unchecked")
                final E castBestEvent = (E) bestEvent;
                return castBestEvent;
            }
    
        }
    
    }
    

    我相信,这种方法比让RuntimeTypeAdapterFactory 适应您的需求更容易实施。但是,实现自定义类型适配器可能会在读取时检测到不明确的字段,因此不会反序列化每个字段(这会花费更多的堆)。

    上述方法将通过以下测试:

    private static final Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .excludeFieldsWithoutExposeAnnotation()
            .create();
    ...
    try ( final JsonReader jsonReader = open("tradeEvent.json") ) {
        Assertions.assertTrue(gson.<WrapperDto>fromJson(jsonReader, WrapperDto.class).toEvent() instanceof Event.Trade);
    }
    try ( final JsonReader jsonReader = open("errorEvent.json") ) {
        Assertions.assertTrue(gson.<WrapperDto>fromJson(jsonReader, WrapperDto.class).toEvent() instanceof Event.Error);
    }
    Assertions.assertThrows(IllegalStateException.class, () -> {
        try ( final JsonReader jsonReader = open("tradeAndErrorEvent.json") ) {
            gson.<WrapperDto>fromJson(jsonReader, WrapperDto.class).toEvent();
        }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      相关资源
      最近更新 更多