Reputation: 8995
I have a generated JSON which I want to load into plain java classes using Gson
Here is the JSON
{"errorCode":0,"host":"hg124.sg3","wssid":"Cw8HBBq854C","data":{"folder":{"meta":{"fid":0,"total":1,"unread":2,"size":3,"core":4,"fname":5,"totalConv":6},"folders":[["Inbox",1,1,6290,1,"Inbox",1]],"new":[],"del":[]},"cids":["97247592121010"],"icids":["97247592121010"],"cinfos":{"meta":{"cid":0,"icid":1,"crc":2,"total":3,"unread":4,"date":5,"hasAttachment":6,"hasDraft":7,"flaggedMsgs":8,"participantList":9,"subject":10,"snippet":11,"folderCounts":12,"minfos":13},"cinfo":{"new":[["97247592121010","97247592121010","1293085011",1,1,1483880495,false,false,0,[{"name":"","email":"[email protected]"}],"hi","test",[{"fid":"Inbox","total":1,"unread":1}],{"new":[["AFSC8QoAAEnXWHI4LwZZAEo2ayM","AFSC8QoAAEnXWHI4LwZZAEo2ayM",0,"[email protected]","","hi","[email protected]",1483880495,"[email protected]",{"snippet":"test"},"Inbox","Inbox","97247592121010","97247592121010",""]],"mod":[],"del":[]}]],"mod":[],"del":[]}},"msgMeta":{"mid":0,"imid":1,"flags":2,"fromEmail":3,"fromName":4,"subject":5,"xapparentlyto":6,"receivedDate":7,"toEmail":8,"snippet":9,"fid":10,"fname":11,"cid":12,"icid":13,"csid":14},"flagBits":{"isReplied":0,"isFlagged":1,"isRead":2,"isForwarded":3,"hasAttachment":4,"isCommercial":5,"isYahoo":6}}}
I used http://www.jsonschema2pojo.org/ to generate the POJOs
-----------------------------------y.data.folderdata.Cinfo.java-----------------------------------
package y.data.folderdata;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Cinfo {
@SerializedName("new")
@Expose
private List<List<String>> _new = null;
@SerializedName("mod")
@Expose
private List<Object> mod = null;
@SerializedName("del")
@Expose
private List<Object> del = null;
public List<List<String>> getNew() {
return _new;
}
public void setNew(List<List<String>> _new) {
this._new = _new;
}
public List<Object> getMod() {
return mod;
}
public void setMod(List<Object> mod) {
this.mod = mod;
}
public List<Object> getDel() {
return del;
}
public void setDel(List<Object> del) {
this.del = del;
}
}
-----------------------------------y.data.folderdata.Cinfos.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Cinfos {
@SerializedName("meta")
@Expose
private Meta_ meta;
@SerializedName("cinfo")
@Expose
private Cinfo cinfo;
public Meta_ getMeta() {
return meta;
}
public void setMeta(Meta_ meta) {
this.meta = meta;
}
public Cinfo getCinfo() {
return cinfo;
}
public void setCinfo(Cinfo cinfo) {
this.cinfo = cinfo;
}
}
-----------------------------------y.data.folderdata.Data.java-----------------------------------
package y.data.folderdata;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Data {
@SerializedName("folder")
@Expose
private Folder folder;
@SerializedName("cids")
@Expose
private List<String> cids = null;
@SerializedName("icids")
@Expose
private List<String> icids = null;
@SerializedName("cinfos")
@Expose
private Cinfos cinfos;
@SerializedName("msgMeta")
@Expose
private MsgMeta msgMeta;
@SerializedName("flagBits")
@Expose
private FlagBits flagBits;
public Folder getFolder() {
return folder;
}
public void setFolder(Folder folder) {
this.folder = folder;
}
public List<String> getCids() {
return cids;
}
public void setCids(List<String> cids) {
this.cids = cids;
}
public List<String> getIcids() {
return icids;
}
public void setIcids(List<String> icids) {
this.icids = icids;
}
public Cinfos getCinfos() {
return cinfos;
}
public void setCinfos(Cinfos cinfos) {
this.cinfos = cinfos;
}
public MsgMeta getMsgMeta() {
return msgMeta;
}
public void setMsgMeta(MsgMeta msgMeta) {
this.msgMeta = msgMeta;
}
public FlagBits getFlagBits() {
return flagBits;
}
public void setFlagBits(FlagBits flagBits) {
this.flagBits = flagBits;
}
}
-----------------------------------y.data.folderdata.FlagBits.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class FlagBits {
@SerializedName("isReplied")
@Expose
private Integer isReplied;
@SerializedName("isFlagged")
@Expose
private Integer isFlagged;
@SerializedName("isRead")
@Expose
private Integer isRead;
@SerializedName("isForwarded")
@Expose
private Integer isForwarded;
@SerializedName("hasAttachment")
@Expose
private Integer hasAttachment;
@SerializedName("isCommercial")
@Expose
private Integer isCommercial;
@SerializedName("isYahoo")
@Expose
private Integer isYahoo;
public Integer getIsReplied() {
return isReplied;
}
public void setIsReplied(Integer isReplied) {
this.isReplied = isReplied;
}
public Integer getIsFlagged() {
return isFlagged;
}
public void setIsFlagged(Integer isFlagged) {
this.isFlagged = isFlagged;
}
public Integer getIsRead() {
return isRead;
}
public void setIsRead(Integer isRead) {
this.isRead = isRead;
}
public Integer getIsForwarded() {
return isForwarded;
}
public void setIsForwarded(Integer isForwarded) {
this.isForwarded = isForwarded;
}
public Integer getHasAttachment() {
return hasAttachment;
}
public void setHasAttachment(Integer hasAttachment) {
this.hasAttachment = hasAttachment;
}
public Integer getIsCommercial() {
return isCommercial;
}
public void setIsCommercial(Integer isCommercial) {
this.isCommercial = isCommercial;
}
public Integer getIsYahoo() {
return isYahoo;
}
public void setIsYahoo(Integer isYahoo) {
this.isYahoo = isYahoo;
}
}
-----------------------------------y.data.folderdata.Folder.java-----------------------------------
package y.data.folderdata;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Folder {
@SerializedName("meta")
@Expose
private Meta meta;
@SerializedName("folders")
@Expose
private List<List<String>> folders = null;
@SerializedName("new")
@Expose
private List<Object> _new = null;
@SerializedName("del")
@Expose
private List<Object> del = null;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public List<List<String>> getFolders() {
return folders;
}
public void setFolders(List<List<String>> folders) {
this.folders = folders;
}
public List<Object> getNew() {
return _new;
}
public void setNew(List<Object> _new) {
this._new = _new;
}
public List<Object> getDel() {
return del;
}
public void setDel(List<Object> del) {
this.del = del;
}
}
-----------------------------------y.data.folderdata.FolderData.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class FolderData {
@SerializedName("errorCode")
@Expose
private Integer errorCode;
@SerializedName("host")
@Expose
private String host;
@SerializedName("wssid")
@Expose
private String wssid;
@SerializedName("data")
@Expose
private Data data;
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getWssid() {
return wssid;
}
public void setWssid(String wssid) {
this.wssid = wssid;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
}
-----------------------------------y.data.folderdata.Meta.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Meta {
@SerializedName("fid")
@Expose
private Integer fid;
@SerializedName("total")
@Expose
private Integer total;
@SerializedName("unread")
@Expose
private Integer unread;
@SerializedName("size")
@Expose
private Integer size;
@SerializedName("core")
@Expose
private Integer core;
@SerializedName("fname")
@Expose
private Integer fname;
@SerializedName("totalConv")
@Expose
private Integer totalConv;
public Integer getFid() {
return fid;
}
public void setFid(Integer fid) {
this.fid = fid;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getUnread() {
return unread;
}
public void setUnread(Integer unread) {
this.unread = unread;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public Integer getCore() {
return core;
}
public void setCore(Integer core) {
this.core = core;
}
public Integer getFname() {
return fname;
}
public void setFname(Integer fname) {
this.fname = fname;
}
public Integer getTotalConv() {
return totalConv;
}
public void setTotalConv(Integer totalConv) {
this.totalConv = totalConv;
}
}
-----------------------------------y.data.folderdata.Meta_.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Meta_ {
@SerializedName("cid")
@Expose
private Integer cid;
@SerializedName("icid")
@Expose
private Integer icid;
@SerializedName("crc")
@Expose
private Integer crc;
@SerializedName("total")
@Expose
private Integer total;
@SerializedName("unread")
@Expose
private Integer unread;
@SerializedName("date")
@Expose
private Integer date;
@SerializedName("hasAttachment")
@Expose
private Integer hasAttachment;
@SerializedName("hasDraft")
@Expose
private Integer hasDraft;
@SerializedName("flaggedMsgs")
@Expose
private Integer flaggedMsgs;
@SerializedName("participantList")
@Expose
private Integer participantList;
@SerializedName("subject")
@Expose
private Integer subject;
@SerializedName("snippet")
@Expose
private Integer snippet;
@SerializedName("folderCounts")
@Expose
private Integer folderCounts;
@SerializedName("minfos")
@Expose
private Integer minfos;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public Integer getIcid() {
return icid;
}
public void setIcid(Integer icid) {
this.icid = icid;
}
public Integer getCrc() {
return crc;
}
public void setCrc(Integer crc) {
this.crc = crc;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getUnread() {
return unread;
}
public void setUnread(Integer unread) {
this.unread = unread;
}
public Integer getDate() {
return date;
}
public void setDate(Integer date) {
this.date = date;
}
public Integer getHasAttachment() {
return hasAttachment;
}
public void setHasAttachment(Integer hasAttachment) {
this.hasAttachment = hasAttachment;
}
public Integer getHasDraft() {
return hasDraft;
}
public void setHasDraft(Integer hasDraft) {
this.hasDraft = hasDraft;
}
public Integer getFlaggedMsgs() {
return flaggedMsgs;
}
public void setFlaggedMsgs(Integer flaggedMsgs) {
this.flaggedMsgs = flaggedMsgs;
}
public Integer getParticipantList() {
return participantList;
}
public void setParticipantList(Integer participantList) {
this.participantList = participantList;
}
public Integer getSubject() {
return subject;
}
public void setSubject(Integer subject) {
this.subject = subject;
}
public Integer getSnippet() {
return snippet;
}
public void setSnippet(Integer snippet) {
this.snippet = snippet;
}
public Integer getFolderCounts() {
return folderCounts;
}
public void setFolderCounts(Integer folderCounts) {
this.folderCounts = folderCounts;
}
public Integer getMinfos() {
return minfos;
}
public void setMinfos(Integer minfos) {
this.minfos = minfos;
}
}
-----------------------------------y.data.folderdata.MsgMeta.java-----------------------------------
package y.data.folderdata;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MsgMeta {
@SerializedName("mid")
@Expose
private Integer mid;
@SerializedName("imid")
@Expose
private Integer imid;
@SerializedName("flags")
@Expose
private Integer flags;
@SerializedName("fromEmail")
@Expose
private Integer fromEmail;
@SerializedName("fromName")
@Expose
private Integer fromName;
@SerializedName("subject")
@Expose
private Integer subject;
@SerializedName("xapparentlyto")
@Expose
private Integer xapparentlyto;
@SerializedName("receivedDate")
@Expose
private Integer receivedDate;
@SerializedName("toEmail")
@Expose
private Integer toEmail;
@SerializedName("snippet")
@Expose
private Integer snippet;
@SerializedName("fid")
@Expose
private Integer fid;
@SerializedName("fname")
@Expose
private Integer fname;
@SerializedName("cid")
@Expose
private Integer cid;
@SerializedName("icid")
@Expose
private Integer icid;
@SerializedName("csid")
@Expose
private Integer csid;
public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public Integer getImid() {
return imid;
}
public void setImid(Integer imid) {
this.imid = imid;
}
public Integer getFlags() {
return flags;
}
public void setFlags(Integer flags) {
this.flags = flags;
}
public Integer getFromEmail() {
return fromEmail;
}
public void setFromEmail(Integer fromEmail) {
this.fromEmail = fromEmail;
}
public Integer getFromName() {
return fromName;
}
public void setFromName(Integer fromName) {
this.fromName = fromName;
}
public Integer getSubject() {
return subject;
}
public void setSubject(Integer subject) {
this.subject = subject;
}
public Integer getXapparentlyto() {
return xapparentlyto;
}
public void setXapparentlyto(Integer xapparentlyto) {
this.xapparentlyto = xapparentlyto;
}
public Integer getReceivedDate() {
return receivedDate;
}
public void setReceivedDate(Integer receivedDate) {
this.receivedDate = receivedDate;
}
public Integer getToEmail() {
return toEmail;
}
public void setToEmail(Integer toEmail) {
this.toEmail = toEmail;
}
public Integer getSnippet() {
return snippet;
}
public void setSnippet(Integer snippet) {
this.snippet = snippet;
}
public Integer getFid() {
return fid;
}
public void setFid(Integer fid) {
this.fid = fid;
}
public Integer getFname() {
return fname;
}
public void setFname(Integer fname) {
this.fname = fname;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public Integer getIcid() {
return icid;
}
public void setIcid(Integer icid) {
this.icid = icid;
}
public Integer getCsid() {
return csid;
}
public void setCsid(Integer csid) {
this.csid = csid;
}
}
}
but I am getting the following exception when I try to load the json into the POJO classes using the code below
FolderData folderData = gson.fromJson(jsonContent.toString(), y.data.folderdata.FolderData.class);
The exception is:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 561 path $.data.cinfos.cinfo.new[0][9]
I suspect this is because the array inside "new" does not have a name? what do I need to do to successfully load this into the POJO classes?
Upvotes: 0
Views: 174
Reputation: 291
The _new
inside class Cinfo is not a List<List<String>>
. Here's your cinfo:
{
"new": [
["97247592121010", "97247592121010", "1293085011", 1, 1, 1483880495, false, false, 0, [{
"name": "",
"email": "[email protected]"
}], "hi", "test", [{
"fid": "Inbox",
"total": 1,
"unread": 1
}], {
"new": [
["AFSC8QoAAEnXWHI4LwZZAEo2ayM", "AFSC8QoAAEnXWHI4LwZZAEo2ayM", 0, "[email protected]", "", "hi", "[email protected]", 1483880495, "[email protected]", {
"snippet": "test"
}, "Inbox", "Inbox", "97247592121010", "97247592121010", ""]
],
"mod": [],
"del": []
}]
],
"mod": [],
"del": []
}
The _new
inside Cinfo
is actually a List<List<Object>>
, and the inside List<Object>
contains String
, int
, boolean
, JSONArray
, and another Cinfo Object
.
To your question, you can sucessfully load this JSON by modifying _new
of class Cinfo to a List<List<Object>>
, like this:
@SerializedName("new")
@Expose
private List<List<Object>> _new = null;
But you still need to find a way to parse this List<List<Object>>
if you want to access those data effectively.
Upvotes: 1
Reputation: 21125
I am a little bit late.
Such POJO generators are weak because they do not handle polymorphic DTO properties due to more complicated analysis of the given JSONs. Such properties also require custom deserializers to deserialize such properties properly.
First off, the generated POJOs have a few issues:
List<List<String>> _new
must be declared a list of list of some unknown class that could be visible to Gson in order to apply the deserialization strategy. Say, List<List<UnknownElement>> _new
-- this is enough to make Gson work however due to Java generics erasure we can do anything here.getNew
and setNew
method should supply and consume a wildcarded list of lists because one cannot guarantee a concrete class here.Meta.fid
probably should be declared as a String
(see below where the deserializer makes remapping, or another DTO class is required).Meta_.fid
probably should be declared as a String
too (see below -- the same reason).This is just a marker for Gson in order to apply a special deserializer for UnknownElement
in Cinfo
. Note that Gson respects the type of the fields having enough information on the further deserialization. However, since it's just a marker, it must not be instantiated or subclassed (an abstract
class with a private
constructor.
abstract class UnknownElement {
private UnknownElement() {
}
}
I didn't find such a mapping in your code, so just created it myself respecting the given JSON in your question.
final class NameAndEmail {
@SerializedName("name")
@Expose
private String name;
@SerializedName("email")
@Expose
private String email;
String getName() {
return name;
}
String getEmail() {
return email;
}
}
This is actually the heart of the complicated parsing. First, it's stateless and does not require to have more than instance per a class loader. Second, Gson support really cool type inference feature based on TypeToken
s that can hold complex type parameterization where any type can be hold unlike Class
. Third, the deserialization method tries to make a simple analysis of the polymorphic properties and return an Object
instance rather than UnknownElement
. Note that the Gson built below will be bound to this class only, and no UnknownElement
instances will be ever instantiated -- the dummy class is just an uninstantiable marker.
final class UnknownElementDeserializer
implements JsonDeserializer<Object> {
private UnknownElementDeserializer() {
}
private static final JsonDeserializer<Object> unknownElementDeserializer = new UnknownElementDeserializer();
private static final Type listOfUnknownElementsType = new TypeToken<List<UnknownElement>>() {
}.getType();
static JsonDeserializer<Object> getUnknownElementDeserializer() {
return unknownElementDeserializer;
}
@Override
public Object deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) {
if ( element.isJsonNull() ) {
return null;
}
if ( element.isJsonPrimitive() ) {
return context.deserialize(element, String.class);
}
if ( element.isJsonObject() ) {
final JsonObject object = element.getAsJsonObject();
if ( isNameAndEmail(object) ) {
return context.deserialize(element, NameAndEmail.class);
}
if ( isMeta_(object) ) {
return context.deserialize(object, Meta_.class);
}
if ( isMeta(object) ) {
return context.deserialize(element, Meta.class);
}
if ( isCinfo(object) ) {
return context.deserialize(object, Cinfo.class);
}
throw new JsonParseException("Can't parse: " + element);
}
if ( element.isJsonArray() ) {
return context.deserialize(element, listOfUnknownElementsType);
}
throw new AssertionError("Must never happen");
}
private static boolean isCinfo(final JsonObject object) {
return object.has("new") && object.has("del");
}
private static boolean isMeta(final JsonObject object) {
return object.has("fid") && object.has("total") && object.has("unread");
}
private static boolean isMeta_(final JsonObject object) {
return object.has("snippet");
}
private static boolean isNameAndEmail(final JsonObject object) {
return object.has("name") && object.has("email");
}
}
And the demo itself that reads and outputs $.data.cinfos.cinfo.new
that have broken your generated POJOs.
public final class EntryPoint {
private EntryPoint() {
}
public static void main(final String... args)
throws IOException {
final Gson gson = new GsonBuilder()
.registerTypeAdapter(UnknownElement.class, getUnknownElementDeserializer())
.create();
try ( final InputStream inputStream = EntryPoint.class.getClassLoader().getResourceAsStream("q41533067/test.json");
final Reader reader = new InputStreamReader(inputStream) ) {
final FolderData folderData = gson.fromJson(reader, FolderData.class);
Stream.of(folderData.getData().getCinfos().getCinfo().getNew())
.flatMap(Collection::stream)
.flatMap((Function<List<?>, Stream<?>>) Collection::stream)
.flatMap(o -> o == null ? empty()
: o instanceof Collection ? ((Collection<?>) o).stream()
: Stream.of(o)
)
.forEach(out::println);
}
}
}
Output:
97247592121010
97247592121010
1293085011
1
1
1483880495
false
false
0
q41533067.NameAndEmail@71dac704
hi
test
q41533067.Meta@123772c4
q41533067.Cinfo@2d363fb3
Just don't trust automated POJO generators and consider reworking the generated POJOs in order to make them work as expected.
Upvotes: 0