我有一堆模型类,其中包含List<X>
类型的字段,其中X
是众多事物之一(例如String
,Integer
,但也有我自己的一些类型)。我使用GSON来解析这些模型的JSON表示。
我的问题是我处理的服务器(我无法控制)以某种方式删除单例数组并用包含的对象替换它们。
例如,而不是返回:
{
"foo": [ "bar"],
"bleh": [ { "some": "object" } ]
}
它返回:
{
"foo": "bar",
"bleh": { "some": "object" }
}
现在假设Java模型类看起来像这样:
public class Model {
private List<String> foo;
private List<SomeObject> bleh;
}
目前这会导致GSON抛出异常,因为它会发现BEGIN_STRING
或BEGIN_OBJECT
所需的BEGIN_ARRAY
。
对于数组或字符串列表,可以使用TypeAdapter<List<String>>
轻松解决。但问题是我List
有许多不同的元素类型,我不想为每个案例写一个单独的TypeAdapter
。我也没有能够使用通用TypeAdapter<List<?>>
,因为在某些时候你需要知道类型。
那么还有另一种方法可以将GSON配置为足够聪明以将单个对象或值转换为数组/列表吗?或者换句话说,只是&#34;假装&#34; [
和]
在哪里可以找到它们,尽管它们不在那里?
答案 0 :(得分:18)
但问题是我有许多不同元素类型的列表,我不想为每种情况编写单独的TypeAdapter。我也没有能够使用通用的TypeAdapter&gt;,因为在某些时候你需要知道类型。
这是适配器工厂的类型:您可以控制Gson
实例配置中的每种类型。
final class AlwaysListTypeAdapterFactory<E>
implements TypeAdapterFactory {
// Gson can instantiate it itself
private AlwaysListTypeAdapterFactory() {
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// If it's not a List -- just delegate the job to Gson and let it pick the best type adapter itself
if ( !List.class.isAssignableFrom(typeToken.getRawType()) ) {
return null;
}
// Resolving the list parameter type
final Type elementType = resolveTypeArgument(typeToken.getType());
@SuppressWarnings("unchecked")
final TypeAdapter<E> elementTypeAdapter = (TypeAdapter<E>) gson.getAdapter(TypeToken.get(elementType));
// Note that the always-list type adapter is made null-safe, so we don't have to check nulls ourselves
@SuppressWarnings("unchecked")
final TypeAdapter<T> alwaysListTypeAdapter = (TypeAdapter<T>) new AlwaysListTypeAdapter<>(elementTypeAdapter).nullSafe();
return alwaysListTypeAdapter;
}
private static Type resolveTypeArgument(final Type type) {
// The given type is not parameterized?
if ( !(type instanceof ParameterizedType) ) {
// No, raw
return Object.class;
}
final ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getActualTypeArguments()[0];
}
private static final class AlwaysListTypeAdapter<E>
extends TypeAdapter<List<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private AlwaysListTypeAdapter(final TypeAdapter<E> elementTypeAdapter) {
this.elementTypeAdapter = elementTypeAdapter;
}
@Override
public void write(final JsonWriter out, final List<E> list) {
throw new UnsupportedOperationException();
}
@Override
public List<E> read(final JsonReader in)
throws IOException {
// This is where we detect the list "type"
final List<E> list = new ArrayList<>();
final JsonToken token = in.peek();
switch ( token ) {
case BEGIN_ARRAY:
// If it's a regular list, just consume [, <all elements>, and ]
in.beginArray();
while ( in.hasNext() ) {
list.add(elementTypeAdapter.read(in));
}
in.endArray();
break;
case BEGIN_OBJECT:
case STRING:
case NUMBER:
case BOOLEAN:
// An object or a primitive? Just add the current value to the result list
list.add(elementTypeAdapter.read(in));
break;
case NULL:
throw new AssertionError("Must never happen: check if the type adapter configured with .nullSafe()");
case NAME:
case END_ARRAY:
case END_OBJECT:
case END_DOCUMENT:
throw new MalformedJsonException("Unexpected token: " + token);
default:
throw new AssertionError("Must never happen: " + token);
}
return list;
}
}
}
现在你只需要告诉Gson 哪些字段格式不正确。
当然,您可以将整个Gson
实例配置为接受此类列表,但使用@JsonAdapter
注释更准确:
final class Model {
@JsonAdapter(AlwaysListTypeAdapterFactory.class)
final List<String> foo = null;
@JsonAdapter(AlwaysListTypeAdapterFactory.class)
final List<SomeObject> bleh = null;
@Override
public String toString() {
return "Model{" + "foo=" + foo + ", bleh=" + bleh + '}';
}
}
final class SomeObject {
final String some = null;
@Override
public String toString() {
return "SomeObject{" + "some='" + some + '\'' + '}';
}
}
测试数据:
{
"foo": "bar",
"bleh": {"some": "object"}
}
{
"foo": ["bar"],
"bleh": [{"some": "object"}]
}
示例:
private static final Gson gson = new Gson();
public static void main(final String... args)
throws IOException {
for ( final String resource : ImmutableList.of("single.json", "list.json") ) {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43412261.class, resource) ) {
final Model model = gson.fromJson(jsonReader, Model.class);
System.out.println(model);
}
}
}
输出:
模型{foo = [bar],bleh = [SomeObject {some ='object'}]}
模型{foo = [bar],bleh = [SomeObject {some ='object'}]}
答案 1 :(得分:2)
您只需编写自己的JsonDeserializer,即可检查template header
name
package test;
template "test template"
rule "test rule"
when
$fact : Fact(code != ) // expression is wrong intentionally
then
$fact.setOk(false);
end
end template
或bleh
是JsonObjects还是JsonArrays。
检查JsonElement是数组还是对象:
foo
答案 2 :(得分:0)
使用GSON库时,您只需检查以下标记是对象还是数组即可。当然,这需要您在解析XML时更加细化,但是它使您可以完全控制要从中获得什么。有时我们不受XML的控制,它可能派上用场。
这是使用JsonReader
类分析文件来检查下一个标记是对象还是数组的示例:
if (jsonReader.peek() == JsonToken.BEGIN_ARRAY) {
jsonReader.beginArray()
} else if (jsonReader.peek() == JsonToken.BEGIN_OBJECT) {
jsonReader.beginObject()
}
在数组/对象的末尾,您可以执行相同的操作,但对于结束标记:
if (jsonReader.peek() == JsonToken.END_ARRAY) {
jsonReader.endArray()
} else if (jsonReader.peek() == JsonToken.END_OBJECT) {
jsonReader.endObject()
}
这样,您可以使用相同的代码(添加额外的检查以验证您是在数组还是对象上)来解析对象数组或单个对象。
答案 3 :(得分:0)
我在使用供应商提供的xml / json时也遇到了同样的问题-他们当然不会为我更改代码:)在更改适应他们自己的版本{{之前, 3}}。我花了一些时间查看gson代码,并找到了许多我想访问的私有变量。因此,从本质上讲,我的自定义集合适配器所做的就是偷看下一个元素是否是一个对象。如果没有,我们只将读取委托给先前的适配器(已被覆盖)。
如果下一个元素是一个对象,我们将使用gson进行处理。然后,我们将其转换为一个对象的数组。使用gson将其写入字符串,然后将该字符串作为JsonReader传递给基础适配器。然后,可以创建基础列表的实例并添加我们拥有的一个元素。
这是AdapterTypeFactory:
public enum ListSingleObjectAdapterFactory implements TypeAdapterFactory {
INSTANCE; // Josh Bloch's Enum singleton pattern
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
TypeAdapter collectionAdapter = gson.getDelegateAdapter(this, typeToken);
Class genericClass = (Class) ((ParameterizedType) typeToken.getType())
.getActualTypeArguments()[0];
return new SingleObjectOrCollectionAdapter(
gson, collectionAdapter, genericClass);
}
}
然后我拥有的类型适配器是:
public class SingleObjectOrCollectionAdapter<T> extends TypeAdapter<Collection<T>> {
private Class<T> adapterclass;
private Gson gson;
private TypeAdapter arrayTypeAdapter;
public SingleObjectOrCollectionAdapter(Gson gson, TypeAdapter<T> collectionTypeAdapter, Class<T> componentType) {
arrayTypeAdapter = collectionTypeAdapter;
this.gson = gson;
adapterclass = componentType;
}
@Override
public Collection<T> read(JsonReader reader) throws IOException {
Collection<T> collection;
JsonReader myReader = reader;
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
T inning = gson.fromJson(reader, adapterclass);
String s = gson.toJson(new Object[]{inning});
myReader = new JsonReader(new StringReader(s));
}
collection = (Collection)arrayTypeAdapter.read( myReader );
return collection;
}
@Override
public void write(JsonWriter writer, Collection<T> value) throws IOException {
arrayTypeAdapter.write(writer, value);
}
}
最后,我们需要注册适配器工厂:
GsonBuilder gb = new GsonBuilder().registerTypeAdapterFactory(ListSingleObjectAdapterFactory.INSTANCE);
到目前为止,似乎可以很好地处理单个和多个对象-尽管如果需要进行一些调整,我不会感到惊讶。