![](/img/trans.png)
[英]Using ModelMapper on different data types with same attribute name
[英]How to handle different data types with same attribute name with Gson?
我目前正在使用Gson在Java中編寫RSS提要解析器。 我正在將RSS'XML轉換為JSON,然后使用Gson將JSON反序列化為Java POJO(有點迂回,但有一個原因)。 對於下面列出的Feed#1( BBC )進行反序列化,一切都運行正常,但對於下面列出的Feed#2( NPR ),我開始拋出異常。
我想我已經確定了問題,但我不確定如何解決它:
問題出現在這兩個RSS源(例如):
對於這些不同的RSS源,稱為“guid”的字段作為a)具有2個字段的對象 (如在BBC RSS Feed中)或b) 字符串 (如在NPR RSS Feed中)返回。
以下是相關JSON的一些釋義版本:
BBC RSS Feed
// is returning 'guid' as an object
"item" :
[
{
// omitted other fields for brevity
"guid" : {
"isPermalink" : false,
"content" : "http:\/\/www.bbc.co.uk\/news\/uk-england-33745057"
},
},
{
// ...
}
]
NPR RSS Feed
// is returning 'guid' as a string
"item" :
[
{
// omitted other fields for brevity
"guid" : "http:\/\/www.npr.org\/sections\/thetwo-way\/2015\/07\/31\/428188125\/chimps-in-habeas-corpus-case-will-no-longer-be-used-for-research?utm_medium=RSS&utm_campaign=news"
},
{
// ...
}
]
我在Java中用這樣建模:
// RSSFeedItem.java
private Guid guid;
// GUID.java
private boolean isPermalink;
private String content;
所以在這種情況下,它可以完美地調用
Gson gson = new Gson();
RssFeed rssFeed = gson.fromJson(jsonData, RssFeed.class);
對於BBC RSS提要,但在解析NPR RSS提要時會拋出異常。
導致我得出這個類型錯誤的結論的具體錯誤如下(當試圖反序列化NPR RSS提要時):
Severe: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was STRING at line 1 column 673 path
$.rss.channel.item[0].guid
所以,無論如何,關鍵是: 我如何處理Gson的這種情況,其中一個字段作為可能不同的數據類型被返回? 我猜可能有某種技巧或注釋我可以使用這種效果,但我不確定,在檢查了Gson的文檔后,我找不到一個現成的答案。
我的答案是使用類層次結構。
abstract class Guid {
private boolean isPermalink;
private String content;
// getters and setters omitted
}
class GuidObject extends Guid {}
class GuidString extends Guid {}
class RssFeedItem {
// super class to receive instances of sub classes
private Guid guid;
}
並為Guid
注冊解串器:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Guid.class, new JsonDeserializer<Guid>() {
@Override
public Guid deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Dispatch based on the type of json
if (json.isJsonObject()) {
// If it's an object, it's essential we deserialize
// into a sub class, otherwise we'll have an infinite loop
return context.deserialize(json, GuidObject.class);
} else if (json.isJsonPrimitive()) {
// Primitive is easy, just set the most
// meaningful field. We can also use GuidObject here
// But better to keep it clear.
Guid guid = new GuidString();
guid.setContent(json.getAsString());
return guid;
}
// Cannot parse, throw exception
throw new JsonParseException("Expected Json Object or Primitive, was " + json + ".");
}
});
這樣您就可以處理更復雜的JSON對象,並根據您喜歡的任何條件進行調度。
您可以使用TypeAdapter
。 我們的想法是只在不同的情況(字符串或對象)之間進行選擇,並委托實際的反序列化。
注冊工廠:
public class RSSFeedItem {
@JsonAdapter(GuidAdapterFactory.class)
private Guid guid;
}
這會創建適配器:
public class GuidAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return (TypeAdapter<T>) new GuidAdapter(gson);
}
}
這決定了如何處理guid:
public class GuidAdapter extends TypeAdapter<Guid> {
private final Gson gson;
public GuidAdapter(Gson gson) {
this.gson = gson;
}
@Override
public void write(JsonWriter jsonWriter, Guid guid) throws IOException {
throw new RuntimeException("Not implemented");
}
@Override
public Guid read(JsonReader jsonReader) throws IOException {
switch (jsonReader.peek()) {
case STRING:
// only a String, create the object
return new Guid(jsonReader.nextString(), true);
case BEGIN_OBJECT:
// full object, forward to Gson
return gson.fromJson(jsonReader, Guid.class);
default:
throw new RuntimeException("Expected object or string, not " + jsonReader.peek());
}
}
}
幾點評論:
它只能工作,因為適配器已注冊屬性。 在委托實際反序列化時,全局注冊會觸發遞歸調用。
只需要工廠,因為我們需要引用Gson
對象,否則我們可以直接注冊適配器類。
我相信TypeAdapter
比Deserializer
更有效,因為它不需要構建JsonElement
樹,盡管在這種情況下差異可能是微不足道的。
根據調用將其設置為Object Class而不是Other Class Type和Type cast
// RSSFeedItem.java
private Object guid;
這是我的示例代碼,希望您覺得它有用
public <T> List<T> readData(InputStream inputStream, Class<T> clazz) throws Exception {
ArrayList<Object> arrayList = new ArrayList<>();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
JsonReader jsonReader = new JsonReader(new InputStreamReader(inputStream, "UTF_8"));
jsonReader.setLenient(true);
JsonToken jsonToken = jsonReader.peek();
switch (jsonToken) {
case BEGIN_ARRAY:
jsonReader.beginArray();
while (jsonReader.hasNext()) {
arrayList.add(gson.fromJson(jsonReader, clazz));
}
jsonReader.endArray();
break;
case BEGIN_OBJECT:
T data = clazz.cast(gson.fromJson(jsonReader, clazz));
arrayList.add(data);
break;
case NUMBER:
Integer number = Integer.parseInt(jsonReader.nextString());
arrayList.add(number);
break;
default:
jsonReader.close();
inputStream.close();
return Collections.emptyList();
}
jsonReader.close();
inputStream.close();
return (List<T>) arrayList;
}
另一個是parseRecursive
中的Streams.java
(你可以谷歌搜索)如下:
private static JsonElement parseRecursive(JsonReader reader)
throws IOException {
switch (reader.peek()) {
case STRING:
return new JsonPrimitive(reader.nextString());
case NUMBER:
String number = reader.nextString();
return new JsonPrimitive(JsonPrimitive.stringToNumber(number));
case BOOLEAN:
return new JsonPrimitive(reader.nextBoolean());
case NULL:
reader.nextNull();
return JsonNull.createJsonNull();
case BEGIN_ARRAY:
JsonArray array = new JsonArray();
reader.beginArray();
while (reader.hasNext()) {
array.add(parseRecursive(reader));
}
reader.endArray();
return array;
case BEGIN_OBJECT:
JsonObject object = new JsonObject();
reader.beginObject();
while (reader.hasNext()) {
object.add(reader.nextName(), parseRecursive(reader));
}
reader.endObject();
return object;
case END_DOCUMENT:
case NAME:
case END_OBJECT:
case END_ARRAY:
default:
throw new IllegalArgumentException();
}
}
更新:您還可以在Streams
類中引用parse(JsonReader reader)
(gson-2.3.1.jar)
像這樣
JsonElement jsonElement = Streams.parse(jsonReader);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.