JsonDeserializer of nested json array (multiple type object) in retrofit2

My json response

[
     [
        {
          "header_id": 1,
          "name" : "Tom",
          "group" : "A"
        },
        {
          "header_id": 2,
          "name" : "Marry",
          "group" : "B"
         }
    ],

    [
         {
             "object_id" : 1,
             "SerialNo" : 123456
         },
         {
             "object_id" : 2,
             "SerialNo" : 89545
         }
     ],

     [
        {
           "workflow_id" : 1,
           "StatusName" : "start"
         },
         {
            "workflow_id" : 2,
            "StatusName" : "end"
          }

    ]
]

Header.class

public class Header{
    @SerializedName("header_id")
    private int HeaderID;
    @SerializedName("name")
    private String name;
    @SerializedName("group")
    private String group;
}

ObjectWork.class

public class ObjectWork {
    @SerializedName("object_id")
    private int ObjectID;
    @SerializedName("SerialNo")
    private int SerialNo;
}

WorkFlow.class

public class WorkFlow{
    @SerializedName("workflow_id")
    private int WorkflowID;
    @SerializedName("StatusName")
     private String statusName;
}

In json array , there are three type json object. How will I design pojo and custom JsonDeserializer in retrofit2. Now I use gson to parse this. I don't know to design class for support this json and deserializer it to pojo model.

Thankyou

Upvotes: 0

Views: 768

Answers (2)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21105

Introduce a common wrapper to let Gson recognize your since Gson prohibits java.lang.Object overriding (note the recommended fieldNamesCaseThatIsAlwaysCamelCase):

abstract class AbstractCommon {
}
final class Header
        extends AbstractCommon {

    @SerializedName("header_id")
    final int headerId = Integer.valueOf(0);

    @SerializedName("name")
    final String name = null;

    @SerializedName("group")
    final String group = null;

    @Override
    public String toString() {
        return headerId + ": " + name;
    }

}
final class ObjectWork
        extends AbstractCommon {

    @SerializedName("object_id")
    final int objectId = Integer.valueOf(0);

    @SerializedName("SerialNo")
    final int serialNo = Integer.valueOf(0);

    @Override
    public String toString() {
        return objectId + ": " + serialNo;
    }

}
final class WorkFlow
        extends AbstractCommon {

    @SerializedName("workflow_id")
    final int workflowId = Integer.valueOf(0);

    @SerializedName("StatusName")
    final String statusName = null;

    @Override
    public String toString() {
        return workflowId + ": " + statusName;
    }

}

Now introduce a JsonDeserializer that would try to recognize actual type:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(AbstractCommon.class, (JsonDeserializer<AbstractCommon>) (jsonElement, type, context) -> {
            final JsonObject jsonObject = jsonElement.getAsJsonObject();
            if ( jsonObject.has("header_id") ) {
                return context.deserialize(jsonElement, Header.class);
            }
            if ( jsonObject.has("object_id") ) {
                return context.deserialize(jsonElement, ObjectWork.class);
            }
            if ( jsonObject.has("workflow_id") ) {
                return context.deserialize(jsonElement, WorkFlow.class);
            }
            throw new IllegalArgumentException("Cannot recognize: " + jsonElement);
        })
        .create();

Now you can easily do:

private static final Type commonsType = new TypeToken<List<List<AbstractCommon>>>() {
}.getType();

public static void main(final String... args)
        throws IOException {
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44034331.class, "arrays.json") ) {
        final List<List<AbstractCommon>> commons = gson.fromJson(jsonReader, commonsType);
        commons.stream()
                .flatMap(Collection::stream)
                .forEach(abstractCommon -> System.out.println(abstractCommon.getClass().getSimpleName() + ": " + abstractCommon));
    }
}

Output:

Header: 1: Tom
Header: 2: Marry
ObjectWork: 1: 123456
ObjectWork: 2: 89545
WorkFlow: 1: start
WorkFlow: 2: end

Frankly speaking I find your JSON a bit weird (assuming the subarrays elements really look related to each other but deconstructed): it's somewhat like an inside-out by structure. If you can control the JSON format, I really commend you to redesign it. If it's not under your control, it looks like you'd need something like this (that's also possible with Gson type adapters):

final class Wrapper {

    final Header header;
    final ObjectWork objectWork;
    final WorkFlow workFlow;

    Wrapper(final Header header, final ObjectWork objectWork, final WorkFlow workFlow) {
        this.header = header;
        this.objectWork = objectWork;
        this.workFlow = workFlow;
    }

}
private static Collection<Wrapper> combine(final List<List<AbstractCommon>> commons) {
    @SuppressWarnings({ "unchecked", "rawtypes" })
    final List<Header> headers = (List) commons.get(0);
    @SuppressWarnings({ "unchecked", "rawtypes" })
    final List<ObjectWork> objectWorks = (List) commons.get(1);
    @SuppressWarnings({ "unchecked", "rawtypes" })
    final List<WorkFlow> workFlows = (List) commons.get(2);
    final Iterator<Header> headerIterator = headers.iterator();
    final Iterator<ObjectWork> objectWorkIterator = objectWorks.iterator();
    final Iterator<WorkFlow> workFlowIterator = workFlows.iterator();
    final Collection<Wrapper> wrappers = new ArrayList<>();
    while ( headerIterator.hasNext() && objectWorkIterator.hasNext() && workFlowIterator.hasNext() ) {
        final Header header = headerIterator.next();
        final ObjectWork objectWork = objectWorkIterator.next();
        final WorkFlow workFlow = workFlowIterator.next();
        final Wrapper wrapper = new Wrapper(header, objectWork, workFlow);
        wrappers.add(wrapper);
    }
    return wrappers;
}
combine(commons)
        .forEach(wrapper -> System.out.println(wrapper.header + ", " + wrapper.objectWork + ", " + wrapper.workFlow));

Output:

1: Tom, 1: 123456, 1: start
2: Marry, 2: 89545, 2: end

Upvotes: 2

K P
K P

Reputation: 182

Follow link to make a pojo directly from JSON:http://www.jsonschema2pojo.org/

package com.example;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Example {

@SerializedName("header_id")
@Expose
private Integer headerId;
@SerializedName("name")
@Expose
private String name;
@SerializedName("group")
@Expose
private String group;

public Integer getHeaderId() {
return headerId;
}

public void setHeaderId(Integer headerId) {
this.headerId = headerId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGroup() {
return group;
}

public void setGroup(String group) {
this.group = group;
}

}

Upvotes: 0

Related Questions