JSON conversion to a specific format - json

I have a Java Object and trying to convert this to a specific JSON format
public class Audit {
String auditId;
String auditData;
}
and above object needs to convert to below JSON format
{
"event":"auditId=100,auditData=purchase order"
}
how do we convert above format using Jackson parser

You need to write a custom serializer for Audit class.
public class AuditSerializer extends StdSerializer<Audit> {
public AuditSerializer() {
super(Audit.class);
}
protected AuditSerializer(Class<Audit> auditClass) {
super(auditClass);
}
#Override
public void serialize(Audit audit, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
String key = "event";
String value = String.format("auditId=%s,auditData=%s", audit.auditId, audit.auditData);
gen.writeStringField(key, value);
gen.writeEndObject();
}
}
And then use this custom serializer to get JSON string for Audit:
SimpleModule module = new SimpleModule();
module.addSerializer(new AuditSerializer(Audit.class));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Audit audit = new Audit();
audit.auditId = "100";
audit.auditData = "purchase Order";
System.out.println(mapper.writeValueAsString(audit));
// {"event":"auditId=100,auditData=purchase Order"}
If you annotate Audit class with #JsonSerialize(using=AuditSerializer.class), then you do not need to explicitly register AuditSerializer.
#JsonSerialize(using=AuditSerializer.class)
public class Audit {
...
And you use ObjectMapper directly.
Audit audit = new Audit();
audit.auditId = "100";
audit.auditData = "purchase Order";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(audit));
// {"event":"auditId=100,auditData=purchase Order"}

Related

How to dynamically ignore a property on Jackson serialization

I have an entity with multiple #ManyToOne associations. I am using spring-boot to expose a REST API. Currently, I have multiple REST API's which return a JSON response of the whole entity, including associations.
But I don't want to serialize all associated objects in all REST APIs.
For example
API-1 should return parent + associationA object
API-2 should return parent + associationA + associationB object
API-3 should return parent + associationB + associationC + associationD
So, in my serialization process, I want to ignore all association except associationA for API-1.
For API-2 I want to ignore other associations except A and B
How do I dynamically ignore these properties during Jackson serialization?
Notes:
I'm using the same class for each; I am not interested in creating a DTO for each API.
Any suggestions are kingly appreciated.
I've put together three approaches for performing dynamic filtering in Jackson. One of them must suit your needs.
Using #JsonView
You could use #JsonView:
public class Views {
interface Simple { }
interface Detailed extends Simple { }
}
public class Foo {
#JsonView(Views.Simple.class)
private String name;
#JsonView(Views.Detailed.class)
private String details;
// Getters and setters
}
#RequestMapping("/foo")
#JsonView(Views.Detailed.class)
public Foo getFoo() {
Foo foo = new Foo();
return foo;
}
Alternatively you can set the view dynamically with MappingJacksonValue.
#RequestMapping("/foo")
public MappingJacksonValue getFoo() {
Foo foo = new Foo();
MappingJacksonValue result = new MappingJacksonValue(foo);
result.setSerializationView(Views.Detailed.class);
return result;
}
Using a BeanSerializerModifier
You could extend BeanSerializerModifier and then override the changeProperties() method. It allows you to add, remove or replace any of properties for serialization, according to your needs:
public class CustomSerializerModifier extends BeanSerializerModifier {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
// In this method you can add, remove or replace any of passed properties
return beanProperties;
}
}
Then register the serializer as a module in your ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new CustomSerializerModifier());
}
});
Check examples here and here.
Using #JsonFilter with a SimpleBeanPropertyFilter
Another approach involves #JsonFilter:
#JsonFilter("customPropertyFilter")
public class Foo {
private String name;
private String details;
// Getters and setters
}
Extend SimpleBeanPropertyFilter and override the serializeAsField() method according to your needs:
public class CustomPropertyFilter extends SimpleBeanPropertyFilter {
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider,
PropertyWriter writer) throws Exception {
// Serialize a field
// writer.serializeAsField(pojo, jgen, provider, writer);
// Omit a field from serialization
// writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
Then register the filter in your ObjectMapper:
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("customPropertyFilter", new CustomPropertyFilter());
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
If you want to make your filter "global", that is, to be applied to all beans, you can create a mix-in class and annotate it with #JsonFilter("customPropertyFilter"):
#JsonFilter("customPropertyFilter")
public class CustomPropertyFilterMixIn {
}
Then bind the mix-in class to Object:
mapper.addMixIn(Object.class, CustomPropertyFilterMixIn.class);
public static <T> String getNonNullFieldsSerialized(T object, ObjectMapper objectMapper)throws JsonProcessingException {
Map<String, Object> objectMap = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() {});
Map<String, Object> objectMapNonNullValues = objectMap.entrySet().stream()
.filter(stringObjectEntry -> Objects.nonNull(stringObjectEntry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return objectMapper.writeValueAsString(objectMapNonNullValues);
}
This will basically ignore all the fields that are non-null. Similarly you can ignore other fields by changing the map filter condition.
I have implemented dynamic filter on data getting from db and returning it using rest api.I have avoided using MappingJacksonValue.As it was getting issue while object chaining
#GetMapping("/courses")
public ResponseEntity<JpaResponse> allCourse() throws Exception {
JpaResponse response = null;
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
List<Course> course = service.findAllCourse();
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name","reviews");
FilterProvider filterProvider = new SimpleFilterProvider().addFilter("jpafilter", filter).setFailOnUnknownId(false);
ObjectWriter writer = mapper.writer(filterProvider);
String writeValueAsString = writer.writeValueAsString(course);
List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
response = new JpaResponse(HttpStatus.OK.name(),resultcourse);
return new ResponseEntity<>(response, HttpStatus.OK);
}
public class JpaResponse {
private String status;
private Object data;
public JpaResponse() {
super();
}
public JpaResponse(String status, Object data) {
super();
this.status = status;
this.data = data;
}
}

How to change the data fomat of the JSON node while serializing and deserializing in jackson

I have following Incoming json, which I deserialize to Model.java and then copy that java object to ModelView.java. I wan't to convert date from String to milliseconds and send the Outgoing json as response.
How do I go for it ?
I've specific reason to copy the value from Model.java to ModelView.java using object mapper. So please don't suggest to modify that part. I'm looking to do this via annotation. I'm pretty sure that it can be done, but don't know how.
The json provided here is a simplistic one. I have a large json in actual scenario.
Incoming json
{
"date":"2016-03-31"
}
Outgoing Json
{
"date":236484625196
}
My Controller Class
#Controller
public class SomeController extends BaseController {
#Autowired
private SomeService someService;
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public #ResponseBody
ResponseEntity<RestResponse> getDetails(HttpServletRequest request, HttpServletResponse response) {
Model model = someService.getData();
ModelView modelView = ModelView.valueOf(model);
return getSuccessResponse(modelView);
}
}
Model.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
private String date;
//normal getters and setters
}
ModelView.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ModelView implements Serializable {
private Long date;
//normal getters and setters
public static ModelView valueOf(Model model){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
ObjectMapper mapper = new ObjectMapper();
ModelView modelView = mapper.convertValue(model, ModelView.class);
try {
modelView.setDate(sdf.parse(model.getDate()).getTime());
} catch (ParseException e) {
IntLogger.error("logging error here");
}
return modelView;
}
}
I'm open to change the variable name from "date" to something else in ModelView.java but the outgoing json should remain same.
Jackson has some build in date formatting, for example, you can set the DateFormatter on the object mapper, but i believe this only works if the serialization and deserialization format is the same.
A simpler approach to date serialization and deserialization, if you want serialization and deserialization to be different format, is to use #JsonSerialize and #JsonDeserialize annotations on your Model.class directly (this could obsolete the need for ModelView if your only purpose was to convert the date).
You can create two classes for serialization and deserialization:
public class JsonDateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateString = jsonParser.getText();
try {
return dateFormat.parse(dateString);
}
catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Then for the serialization to your Outgoing json:
public class JsonDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(Long.toString(date.getTime()));
}
}
Now, you an just annotate your Model.java:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private String date;
//normal getters and setters
}

How to include only specific properties when serializing with Jackson

I am trying to implement a universal method which serializes the given object to JSON, but only those properties which are passed in a collection. If possible I want to get this functionality without specifying #JsonFilter on the class. For this I am trying to use FilterExceptFilter from Jackson 2.4.1. Dependencies:
jackson-core-2.4.1.jar
jackson-databind-2.4.1.jar
jackson-annotations-2.4.0.jar
Here is what I have at the moment:
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) return null;
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter("fieldFilter", filter);
fProvider.setDefaultFilter(filter);
ObjectMapper mapper = new ObjectMapper();
mapper.setFilters(fProvider);
String json = mapper.writeValueAsString(o);
return json;
}
However, the filter is never applied. It always serializes all properties.
Set<String> fields = new HashSet<String>(); fields.add("name");
String json = Serializer.serializeOnlyGivenFields(e, fields);
System.out.println(json);
{"name":"Test entity","description":"Test description"}
I have also tried to register the FilterProvider on the ObjectWriter, but same result:
String json = mapper.writer(fProvider).writeValueAsString(o);
What am I missing? Is there a nice way to achieve this with Jackson?
Based on http://www.cowtowncoder.com/blog/archives/2011/09/entry_461.html an alternate way to set up the filter is setting up a class that extends JacksonAnnotationIntrospector and overrides findFilterId. You can then specify to find your filter in the findFilterId. This could be made to be as robust if you want based on some other map or algorithm. Below is sample code. Not sure if the performance is better than the solution above but it seems to be simpler and probably more easily extensible. I was doing this for serializing CSV using Jackson. Any feedback is welcome!
public class JSON {
private static String FILTER_NAME = "fieldFilter";
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter(FILTER_NAME, filter);
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector( new AnnotationIntrospector() );
String json = mapper.writer(fProvider).writeValueAsString(o);
return json;
}
private static class AnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public Object findFilterId(Annotated a) {
return FILTER_NAME;
}
}
}
One additional thing is that you have to indicate Java classes for which filter is to be used by #JsonFilter annotation:
#JsonFilter("fieldFilter")
public class MyType { }
and then it should apply.
I have found a solution based on Jackson: How to add custom property to the JSON without modifying the POJO. I override BeanSerializer#serializeFields to always use BeanSerializer#serializeFieldsFiltered instead. This way the filter is always applied.
Performance-wise not a very good solution, since an ObjectMapper has to be constructed at every method call. Feel free to post improvements or suggestions!
Module implementation:
public class FilteredModule extends SimpleModule {
private static final long serialVersionUID = 1L;
#Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
#Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config,
BeanDescription beanDesc,
JsonSerializer<?> serializer) {
if (serializer instanceof BeanSerializerBase) {
return new FilteredBeanSerializer(
(BeanSerializerBase) serializer);
}
return serializer;
}
});
}
private class FilteredBeanSerializer extends BeanSerializer {
public FilteredBeanSerializer(BeanSerializerBase source) {
super(source);
}
#Override
protected void serializeFields(Object arg0, JsonGenerator arg1,
SerializerProvider arg2) throws IOException,
JsonGenerationException {
super.serializeFieldsFiltered(arg0, arg1, arg2);
}
}
}
API method:
public static String serializeOnlyGivenFields(Object o,
Collection<String> fields) throws JsonProcessingException {
if ((fields == null) || fields.isEmpty()) fields = new HashSet<String>();
Set<String> properties = new HashSet<String>(fields);
SimpleBeanPropertyFilter filter =
new SimpleBeanPropertyFilter.FilterExceptFilter(properties);
SimpleFilterProvider fProvider = new SimpleFilterProvider();
fProvider.addFilter("fieldFilter", filter);
fProvider.setDefaultFilter(filter);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new FilteredModule());
String json = mapper.writer(fProvider).writeValueAsString(o);
return json;
}
Example
Entity e = new Entity("Test entity", "Test description");
Set<String> fields = new HashSet<String>(); fields.add("name");
String json = JSON.serializeOnlyGivenFields(e, fields);
System.out.println(json);
{"name":"Test entity"}
Benchmark: 1000 iterations on the same object
serializeOnlyGivenFields: 536 ms
serialize (reuses ObjectMapper): 23 ms

Jersey client read json response into custom object

public class RESTDataServiceClient{
private Client client;
private String dataServiceUri;
private String dataServiceResource;
private CustomData customData;
public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
this.client = client;
this.dataServiceUri = dataServiceUri;
this.dataServiceResource = dataServiceResource;
}
#Override
public CustomData getCustomData() {
WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
myCustomData = response.readEntity(CustomData.class);
return myCustomData;
}
}
CustomData.java
public class CustomData{
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
//getter and setter
}
sample json content
{"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }
I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
... 38 more
TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.
It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.
If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:
public class FakeTest {
#Test
public void test() throws Exception {
ObjectMapper om = new ObjectMapper();
om.registerModule(new CustomModule());
String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
CustomData cd = om.readValue(s, CustomData.class);
System.out.println(cd.getData());
}
public static class CustomData {
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }
public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
}
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomData.class, new CustomSerializer());
addDeserializer(CustomData.class, new CustomDeserializer());
}
public static class CustomSerializer extends JsonSerializer<CustomData> {
#Override
public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// add custom serializer here
}
}
public static class CustomDeserializer extends JsonDeserializer<CustomData> {
#Override
public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
ObjectNode value = (ObjectNode) entry.getValue();
Map.Entry<String, JsonNode> innerField = value.fields().next();
ObjectNode innerNode = (ObjectNode) innerField.getValue();
Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
TIntIntMap intMap = new TIntIntHashMap();
intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
data.put(Long.parseLong(entry.getKey()), innerMap);
}
CustomData customData = new CustomData();
customData.setData(data);
return customData;
}
}
}
}

Telling Retrofit to what variable it should map a certain json field?

The REST API I'm talking to is responding to some of the requests in a structure as such:
{
"_links": {
"next": "NEXT_DATA_BLOCK_URL",
"prev": "PREV_DATA_BLOCK_URL",
"self": "CURRENT_DATA_BLOCK_URL"
},
"RESPONSE_DATA_NAME": [
{
... DATA_FIELDS ...
}
]
}
Where 'RESPONSE_DATA_NAME' is the data "name" - changes according to desired request. for example, it might be 'teams' or 'messages'.
Therefore I created a generic class with the following members:
public class PagedResponse<T> {
public PagingLinks _links;
public List<T> _data;
}
Is there any way I can set up my RestAdapter so that it'll always map 'RESPONSE_DATA_NAME' to the '_data' member, no matter what the field name actually is?
Thanks ^_^
Using gson you can annotate your _data field with the #SerializedName. The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field _data is represented as RESPONSE_DATA_NAME in JSON.
public class PagedResponse<T> {
public PagingLinks _links;
#SerializedName(value="RESPONSE_DATA_NAME")
public List<T> _data;
}
Further see doc
If you want to control the json field then you have to write custom de-serializer as like below
public class CustomDeserializer implements JsonDeserializer<PagedResponse> {
#Override
public PagedResponse deserialize(final JsonElement json,
final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
Gson gson = new Gson();
PagedResponse pagedResponse = new PagedResponse<>();
List list = new ArrayList<>();
pagedResponse = gson.fromJson(json, PagedResponse.class);
Type listType = new TypeToken<List>() {}.getType();
Set<Entry<String, JsonElement>> enteries = json.getAsJsonObject().entrySet();
for (Entry<String, JsonElement> entry : enteries) {
JsonElement jsonElement = (JsonElement) entry.getValue();
if (jsonElement.isJsonArray()) {
list.add(gson.fromJson(jsonElement, listType));
}
}
pagedResponse.set_data(list);
return pagedResponse;
}
}
finally parse it
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(PagedResponse.class, new CustomDeserializer());
Gson gson = gsonBuilder.create();
gson.fromJson(Your_JSON_STRING_HERE, PagedResponse.class);
So I finally found a solution to the problem...
I created a costume de-serializer, which adds the data field to the existing JsonObject, and copies the content of the RESPONSE_DATA_NAME (which is a JsonArray).
Then I serialize it normaly with GSON simple conversion (gson.fromJson()).
It's a bit stupid but it works =P
The de-serializer's class:
public class PagedResponseDeserializer implements JsonDeserializer<PagedResponse> {
#Override
public PagedResponse deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
JsonElement value = null;
JsonObject jsonObject = json.getAsJsonObject();
Iterable<Entry<String,JsonElement>> entries = jsonObject.entrySet();
for (Entry<String, JsonElement> entry : entries) {
value = entry.getValue();
if (value.isJsonArray()) break;
}
jsonObject.add("data", value);
return gson.fromJson(jsonObject, typeOfT);
}
}