I'm currently trying to use Flexjson to deserialize a JSON String and map it to the Object model of my Android App. The application is a kind of library with several vendors that can have some catalogs with more catalogs and documents in them. The json is fetched from a web service I have no influence on and looks something like this:
{
"library":{
"vendor":[
{
"id":146,
"title":"Vendor1",
"catalog":[
{
"id":847,
"document":[
{
"id":1628,
"title":"Document",
...
},
{
...
}
],
"title":"Catalog ",
},
{
...
}
]
},
{
...
}
]
}
}
So each vendor, catalog, document is represented by a JSONObject and all child catalogues and documents are within a JSONArray. So far everything works fine with Flexjson and the following deserialization code:
LibraryResponse response = new JSONDeserializer<LibraryResponse>()
.use(Timestamp.class, new TimestampObjectFactory())
.deserialize(getLocalLibrary(), LibraryResponse.class);
return response.library;
I do have a Library object that has a List<Vendor>
. Each vendor has a List<Catalog>
and a List<Document>
.
But unfortunately, the web service straps the JSONArrays to simple JSONObjects if a catalog contains only a single document or a catalog contains just one catalog. So the json in that case looks like this:
"document":
{
"id":1628,
"title":"Document",
...
}
Now Flexjson doesn't know how to deserialize and I end up with a library.vendorX.getDocument() being a List<HashMap>
instead of a List<Document>
.
One idea is to tell Flexjson explicitly how to handle such cases, but I have no idea where to start with this. Another way could be to parse the initial json manually and replace such JSONObjects with the appropriate JSONArray. But I think that way is not really nice to go, as the library can be pretty deep.
I hope you can provide some guidance here.
Yikes this is some gnarly json mapping going on. What backend coder did that?! #NotHelping.
Well from looking at the code, Flexjson is coded to handle this out of the box. But it looks like it's not passing the typing information down to the bind so it doesn't know what type it's binding into so it just returns a Map. That's a bug that should probably be fixed. Good news is there is a work around.
Anyway, the simplest thing I can think of is to install an ObjectFactory on that list. Then you can check and see if you get a Map or a List when the stream is deserialized. Then you can wrap it in a List and send it on to the appropriate decoder. Something like:
LibraryResponse response = new JSONDeserializer<LibraryResponse>()
.use(Timestamp.class, new TimestampObjectFactory())
.use("library.vendor.values.catalog.values.document", new ListDocumentFactory() )
.deserialize(getLocalLibrary(), LibraryResponse.class);
Then
public class ListDocumentFactory implements ObjectFactory {
public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass) {
if( value instanceof Collection ) {
return context.bindIntoCollection((Collection)value, new ArrayList(), targetType);
} else {
List collection = new ArrayList();
if( targetType instanceof ParameterizedType ) {
ParameterizedType ptype = (ParameterizedType) targetType;
collection.add( context.bind(value, ptype.getActualTypeArguments()[0]) );
} else {
collection.add( context.bind( value ) );
return collection;
}
}
}
}
I think that's roughly what would fix that bug, but should also fix your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With