JAVA : How make custom inner class for complex Json - java

I've read a lot posts but don't find how make the class for this json.
{
"snippet": {
"parentGroupId": "69ea5920-0157-1000-0000-0000028e1b90",
"processors": {},
"processGroups": {
"1231-23a": {
"clientId": "50b3ec1a-c123-1e4f-718c-b0323fb1e175",
"version": 0
}
}
}
}
And the problem is that property "1231-23a" can change in my json like this :
{
"snippet": {
"parentGroupId": "69ea5920-0157-1000-0000-0000028e1b90",
"processors": {},
"processGroups": {
"4544-412f": {
"clientId": "50b3ec1a-c123-1e4f-718c-b0323fb1e175",
"version": 0
}
}
}
}
thanks for yours helps

You can use a Map from String to the nested data for example ProcessGroup. Then "1231-23a" and "4544-412f" would be keys in this Map. For example these classes for "snippet" and "processGroups" and add constructors, getters etc
class Snippet {
String parentGroupId;
Processors processors;
Map<String, ProcessGroup> processGroups;
}
class ProcessGroup {
String clientId;
int version;
}

#manos,
It's missing in my classes to work correctly.
snippet
class Snippet {
String parentGroupId;
// Processors processors;
Map<String, ProcessGroup> processGroups;
public void setParentGroupId(String string) {
this.parentGroupId = string ;
}
public void setProcessGroups(Map<String, ProcessGroup> procGps)
{
this.processGroups = procGps;
}
public String toString()
{
String str = "{\n"
+ " \"snippet\": {\n"
+" \"parentGroupId\": \""+ this.parentGroupId +"\",\n"
+" \"processGroups\": \""+ this.processGroups +"\""
+ "\n}";
return str;
}
}
ProcessGroup class
class ProcessGroup {
String clientId;
int version;
public void setClientId(String clientId) {
this.clientId = clientId;
}
public void setVersion(int version) {
this.version = version;
}
public String toString()
{
String str = "\n \"clientId\": \"" + this.clientId +"\"\n "
+" \"version\": "+ this.version;
return str;
}
}
Test class
public class Test {
public static void main(String[] args) {
Map<String, ProcessGroup> map = new HashMap<String, ProcessGroup>();
// ObjectMapper oMapper = new ObjectMapper();
//Student obj = new Student();
Snippet obj = new Snippet();
ProcessGroup objPG = new ProcessGroup();
objPG.setClientId("clientId-10");
objPG.setVersion(12);
obj.setParentGroupId("parentId-10");
map.put("processGroup-10", objPG);
obj.setProcessGroups(map);
// object -> Map
System.out.println(obj);
}
}
result
it seems, i don't have 'access' to processGroup-10 and plus the sign "=" and not ":"
What's wrong ?
{
"snippet": {
"parentGroupId": "parentId-10",
"processGroups": "{processGroup-10=
"clientId": "clientId-10"
"version": 12}"
}

Related

Json nested Objects check if null

consider the following json
{
"sub1":{
"name":"Name-sub1",
"sub11":{
"name":"Name-sub11",
"sub111":{
"name":"Name-sub111",
...
},
..
},
...
},
...
}
I now want to fetch the inner name Element (Name-sub111) in java (it's a io.vertx.core.json.JsonObject)
object.getJsonObject("sub1").getJsonObject("sub11").getJsonObject("sub111").getString("name")
But I'm not sure if sub1 or sub11 or sub111 even exist - so I always need to check for null
if(object.getJsonObject("sub1") != null && object.getJsonObject("sub1").getJsonObject("sub11") != null && object.getJsonObject("sub1").getJsonObject("sub11").getJsonObject("sub111") != null) {
return object.getJsonObject("sub1").getJsonObject("sub11").getJsonObject("sub111").getString("name");
}
Does someone know a better solutions for this case?
You might want to consider creating some helper methods that return Optional objects.
public static Optional<JsonObject> getJsonObject(JsonObject obj, String prop) {
return Optional.ofNullable(obj.getJsonObject(prop));
}
public static Optional<String> getString(JsonObject obj, String prop) {
return Optional.ofNullable(obj.getString(prop));
}
Then you can compose the methods to achieve the desired result.
try {
return getJsonObject(obj, "sub1").map((obj) -> getJsonObject(obj, "sub2")).map((obj) -> getString(obj, "sub3")).get();
} catch (NoSuchElementException e) {
return null;
}
an option here is to use Java 8 Optional, which eliminates the dupplicate method calls. Howewver, the end result might not look clear enough for some:
return Optional.ofNullable(object.getJsonObject("sub1")).map(subObj1 ->
Optional.ofNullable(subObj1.getJsonObject("sub11")).map(subObj11 ->
Optional.ofNullable(subObj11.getJsonObject("sub111")).map(subObj111 -> subObj111.getString("name")
)));
If it's not one-time parsing thing, you can map your json to java object and use Optional class:
import io.vertx.core.json.JsonObject;
import java.util.Optional;
class Scratch {
public static void main(String[] args) {
String json = "{\n" +
" \"sub1\" : {\n" +
" \"name\" : \"Name-sub1\",\n" +
" \"sub11\" : {\n" +
" \"name\" : \"Name-sub11\",\n" +
" \"sub111\" : {\n" +
" \"name\" : \"Name-sub111\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
JsonObject object = new JsonObject(json);
SamplePojo pojo = object.mapTo(SamplePojo.class);
System.out.println(pojo);
String sub111name = pojo.getSub1()
.flatMap(Sub1::getSub11)
.flatMap(Sub11::getSub111)
.map(Sub111::getName)
.orElse("");
System.out.println(sub111name);
}
}
class SamplePojo {
private Sub1 sub1;
public Optional<Sub1> getSub1() {
return Optional.ofNullable(sub1);
}
public void setSub1(Sub1 sub1) {
this.sub1 = sub1;
}
}
class Sub1 {
private String name;
private Sub11 sub11;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Sub11> getSub11() {
return Optional.ofNullable(sub11);
}
public void setSub11(Sub11 sub11) {
this.sub11 = sub11;
}
}
class Sub11 {
private String name;
private Sub111 sub111;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Optional<Sub111> getSub111() {
return Optional.ofNullable(sub111);
}
public void setSub111(Sub111 sub111) {
this.sub111 = sub111;
}
}
class Sub111 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Simple yet efficient solution, using reduction:
List<String> nodesToTraverse = Arrays.asList("sub1", "sub11", "sub111");
JsonObject leaf = nodesToTraverse.stream().reduce(object,
(obj, node) -> obj == null ? null : obj.getJsonObject(node),
(prev, cur) -> cur);
return leaf != null ? leaf.getString("name") : null;
Might not be the best solution, because this Exception could also be caused for another reason.
try {
object.getJsonObject("sub1").getJsonObject("sub11").getJsonObject("sub111").getString("name")
} catch(NullpointerException e) {
// handle error
}

Is it possible to flatten the JSON hierarchy with Gson?

I use Gson to convert JSON data to Java objects. However, the JSON structure has an extra field which could be flattened. Is this possible to do with Gson?
To elaborate (since this is rather difficult to explain), the JSON looks something like this:
{
"foo": "bar",
"data": {
"first": 0,
"second": 1,
"third": 2
}
}
This produces two classes, one for the parent and one for data, like this:
public class Entry {
private String foo;
private Data data;
}
public class Data {
private int first;
private int second;
private int third;
}
I'd like to "flatten" the data field into the parent object so that the Java class would look something like this:
public class Entry {
private String foo;
private int first;
private int second;
private int third;
}
Is this possible with Gson, using e.g. TypeAdapters?
I'll show you demo and you decide for yourself do you really want this... Because it makes TypeAdapter code hard to read.
private static class EntryTypeAdapter extends TypeAdapter<Entry> {
// without registerTypeAdapter(Entry.class, new EntryTypeAdapter())
private Gson gson = new GsonBuilder()
// ignore "foo" from deserialization and serialization
.setExclusionStrategies(new TestExclStrat()).create();
#Override
public void write(JsonWriter out, Entry value) throws IOException {
out.beginObject();
out.name("foo");
out.value(value.foo);
out.name("data");
out.value(gson.toJson(value));
out.endObject();
}
#Override
public Entry read(JsonReader in) throws IOException {
Entry entry = null;
String foo = null;
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
if (name.equals("foo")) {
foo = in.nextString();
} else if (name.equals("data")) {
entry = gson.fromJson(in, Entry.class);
} else {
in.skipValue();
}
}
in.endObject();
if(entry!= null) entry.foo = foo;
return entry;
}
public class TestExclStrat implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> arg0) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
return f.getName().equals("foo");
}
}
}
Can test it with this:
public static void main(String[] args) throws IOException, JSONException {
String jsonString = "{\n" +
" \"foo\": \"bar\",\n" +
" \"data\": {\n" +
" \"first\": 0,\n" +
" \"second\": 1,\n" +
" \"third\": 2\n" +
" }\n" +
"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(Entry.class, new EntryTypeAdapter()).create();
Entry el = gson.fromJson(jsonString, Entry.class);
String serialized = gson.toJson(el);
System.out.println(serialized);
}
public static class Entry {
public String foo;
public Integer first;
public Integer second;
public Integer third;
}
You could also do something like this:
// even more complicated version without inner Gson help
public Entry readOption2(JsonReader in) throws IOException {
Entry entry = new Entry();
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
if (name.equals("foo")) {
entry.foo = in.nextString();
} else if (name.equals("data")) {
in.beginObject();
while (in.hasNext()) {
name = in.nextName();
if (name.equals("first")) {
entry.first = in.nextInt();
} else if (name.equals("second")) {
entry.second = in.nextInt();
} else if (name.equals("third")) {
entry.third = in.nextInt();
}else{
in.skipValue();
}
}
in.endObject();
} else {
in.skipValue();
}
}
in.endObject();
return entry;
}

Create map of maps from eclipse toString()

Eclipse can auto-generate a toString() method from a object's fields. If those fields are objects then they too may have similarly auto-generated toString() methods.
e.g. a President object might look like this:
President [country=USA, name=Name [title=Mr, forename=Barack, surname=Obama], address=Address [houseNumber=1600, street=Pennsylvania Avenue, town=Washington]]
which is easier to read if I format it:
President [
country=USA,
name=Name [
title=Mr,
forename=Barack,
surname=Obama],
address=Address [
houseNumber=1600,
street=Pennsylvania Avenue,
town=Washington]]
What is the best way to parse this String to create a map of maps?
I've got a solution, but it's not pretty. I was hoping to be able to avoid the low level String manipulation somehow, but here it is:
import java.util.LinkedHashMap;
import java.util.Map;
public class MappedObject {
public String className;
public Map<String, String> leafFields = new LinkedHashMap<>();
public Map<String, MappedObject> treeFields = new LinkedHashMap<>();
#Override
public String toString() {
return "[className=" + className
+ (leafFields.isEmpty() ? "" : ", leafFields=" + leafFields)
+ (treeFields.isEmpty() ? "" : ", treeFields=" + treeFields)
+ "]";
}
public static MappedObject createFromString(String s) {
MappedObject mo = new MappedObject();
new Mapper(s).mapObject(mo);
return mo;
}
private static class Mapper {
private String s;
public Mapper(String s) {
this.s = s;
}
private String mapObject(MappedObject mo) {
mo.className = removeFirstNCharacters(s.indexOf(' '));
while (s.contains("=")) {
removeLeadingNonLetters();
String key = removeFirstNCharacters(s.indexOf('='));
removeFirstNCharacters(1); // remove the =
String leafValue = getLeafValue();
if (leafValue != null) {
mo.leafFields.put(key, leafValue);
if (s.startsWith("]")) { // that was the last field in the tree
return s;
}
} else {
MappedObject treeField = new MappedObject();
mo.treeFields.put(key, treeField);
s = new Mapper(s).mapObject(treeField);
}
}
return s; // s contains only close brackets - ]
}
private void removeLeadingNonLetters() {
int i = 0;
while (!Character.isLetter(s.charAt(i))) {
i++;
}
removeFirstNCharacters(i);
}
private String removeFirstNCharacters(int n) {
String value = s.substring(0, n);
s = s.substring(value.length());
return value;
}
private String getLeafValue() {
int endIndex = getEndIndex();
if (!s.contains("[") || s.indexOf('[') > endIndex) {
return removeFirstNCharacters(endIndex);
}
return null;
}
/** The end of the value, if it's a leaf field. */
private int getEndIndex() {
if(s.contains(",")) {
return Math.min(s.indexOf(','), s.indexOf(']'));
}
return s.indexOf(']');
}
}
}

How to read elements of arraylist of a class defined in another class in java?

I am working on a project for my college.
i have two classes as "Email_info" and "contacts". In class "contacts", i made an Arraylist of type "Email_info". This class contacts is used to add data to a XML file("contacts.xml" ), and uses variables of email_info class. The problem is whenever i try to access elements of this "contacts.xml" file after unmarshalling the file, i get address as "mailwidgetaa.Email_info#12d3a4e9" instead of the actual data( which should be like, e_id- abc#gmail.com, pass- password). So how do I do get the actual data ?
below is the full code::
package mailwidgetaa;
#XmlRootElement
public class Contacts {
List<Email_info> contacList = new ArrayList<Email_info>();
#XmlElement
public List<Email_info> getContacList() {
return contacList;
}
public void setContacList(List<Email_info> contacList) {
this.contacList = contacList;
}
}
#XmlRootElement
class Email_info {
String e_id;
String u_name;
String pass;
#XmlElement
public String getE_id() {
return e_id;
}
public void setE_id(String e_id) {
this.e_id = e_id;
}
#XmlElement
public String getU_name() {
return u_name;
}
public void setU_name(String u_name) {
this.u_name = u_name;
}
#XmlElement
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
}
public class Mailwidgetaa {
public static void main(String[] args) {
contatcs con = new contacts();
con = null;
try {
JAXBContext jaxbc1 = JAXBContext.newInstance(Contacts.class);
Unmarshaller unmarsh = jaxbc1.createUnmarshaller();
con = (Contacts) unmarsh.unmarshal(new File("contacts.xml"));
} catch (Exception e) {
System.out.println("Exception " + e.getMessage());
}
Email_info ein = new Email_info();
ein.setE_id(ui);
ein.setU_name(un);
con.getContacList().add(ein);
try {
JAXBContext jaxbc = JAXBContext.newInstance(Contacts.class);
Marshaller marsh = jaxbc.createMarshaller();
marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marsh.marshal(con, new File("contacts.xml"));
} catch (JAXBException e) {
System.out.println("EXCEPTION" + e.getMessage());
}
}
Iterator e= con.contacList.iterator();
while(e.hasNext()){
System.out.println(e.next());
}
}
That's because you haven't implemented toString method in your Email_Info class. Implement it like:
#Override
public String toString() {
return "e_id: " + e_id + " u_name: " + u_name + " pass: " + pass;
}

Parse JSON with no specific structure for a field with GSON

I am working on an Android application, using the EmpireAvenue API.
The API uses JSON and I'm using the GSON library to parse the data from the API.
Here is the problem:
I have a JSON structure like this:
{
type: "earnings",
info: {
earnings: 64.09
dividends: 1277.34
gains: 1997.05
expenses: 4895.51
shares_bought: 210
shares_bought_user_count: 2
shares_sold: 0
shares_sold_user_count: 0
},
created: "2011-04-16 11:32:37"
},
{
type: "following",
info: [
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Recommended Buys"
},
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Watch List"
}
],
created: "2011-04-16 11:00:08"
}
As you can see, the structure associated with the info field is different. Sometimes it's an object, sometimes an array. As expected, the GSON library throws errors when parsing.
Do you know how to parse a JSON structure with when a field changes structure ?
Thanks for your help.
The current solution with Gson is a bit involved, requiring implementation of a custom Instance Creator and/or a custom Deserializer. Take a look at http://code.google.com/p/google-gson/issues/detail?id=231 and the release notes on Hierarchical Type Adapters for details. I just posted an example of polymorphic deserialization with Gson in response to Polymorphism with gson.
Gson hopefully will soon have the RuntimeTypeAdapter for simpler polymorphic deserialization. See http://code.google.com/p/google-gson/issues/detail?id=231 for more info.
On the other hand, a Jackson-based solution isn't so bad.
public class Foo
{
static String jsonInput =
"[" +
"{" +
"\"type\":\"earnings\"," +
"\"info\":" +
"{" +
"\"earnings\":64.09," +
"\"dividends\":1277.34," +
"\"gains\":1997.05," +
"\"expenses\":4895.51," +
"\"shares_bought\":210," +
"\"shares_bought_user_count\":2," +
"\"shares_sold\":0," +
"\"shares_sold_user_count\":0" +
"}," +
"\"created\":\"2011-04-16 11:32:37\"" +
"}," +
"{" +
"\"type\":\"following\"," +
"\"info\":" +
"[" +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyRecommendedBuys\"" +
"}," +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyWatchList\"" +
"}" +
"]," +
"\"created\":\"2011-04-16 11:00:08\"" +
"}" +
"]";
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());
DateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(dataFormat);
Collection<Thing> things = mapper.readValue(jsonInput, new TypeReference<Collection<Thing>>(){});
System.out.println(things);
}
}
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({#Type(value=Earnings.class, name="earnings"), #Type(value=Following.class, name="following")})
abstract class Thing
{
private Date created;
void setCreated(Date created)
{
this.created = created;
}
#Override
public String toString()
{
return String.format(
"[%1$s: created=%2$s, other attributes:%3$s]",
getClass().getSimpleName(), created, toStringAddenda());
}
abstract String toStringAddenda();
}
class Earnings extends Thing
{
private EarningsInfo info;
void setInfo(EarningsInfo info)
{
this.info = info;
}
#Override
String toStringAddenda()
{
return info.toString();
}
}
class Following extends Thing
{
private Collection<FollowingInfo> info;
void setInfo(Collection<FollowingInfo> info)
{
this.info = info;
}
#Override
String toStringAddenda()
{
return info.toString();
}
}
class FollowingInfo
{
private String ticker;
private String fullName;
private String listName;
void setTicker(String ticker)
{
this.ticker = ticker;
}
void setFullName(String fullName)
{
this.fullName = fullName;
}
void setListName(String listName)
{
this.listName = listName;
}
#Override
public String toString()
{
return String.format(
"[FollowingInfo: ticker=%1$s, fullName=%2$s, listName=%3$s]",
ticker, fullName, listName);
}
}
class EarningsInfo
{
private BigDecimal earnings;
private BigDecimal dividends;
private BigDecimal gains;
private BigDecimal expenses;
private int sharesBought;
private int sharesBoughtUserCount;
private int sharesSold;
private int sharesSoldUserCount;
void setEarnings(BigDecimal earnings)
{
this.earnings = earnings;
}
void setDividends(BigDecimal dividends)
{
this.dividends = dividends;
}
void setGains(BigDecimal gains)
{
this.gains = gains;
}
void setExpenses(BigDecimal expenses)
{
this.expenses = expenses;
}
void setSharesBought(int sharesBought)
{
this.sharesBought = sharesBought;
}
void setSharesBoughtUserCount(int sharesBoughtUserCount)
{
this.sharesBoughtUserCount = sharesBoughtUserCount;
}
void setSharesSold(int sharesSold)
{
this.sharesSold = sharesSold;
}
void setSharesSoldUserCount(int sharesSoldUserCount)
{
this.sharesSoldUserCount = sharesSoldUserCount;
}
#Override
public String toString()
{
return String.format(
"[EarningsInfo: earnings=%1$s, dividends=%2$s, gains=%3$s, expenses=%4$s, sharesBought=%5$s, sharesBoughtUserCount=%6$s, sharesSold=%7$s, sharesSoldUserCount=%8$s]",
earnings, dividends, gains, expenses, sharesBought, sharesBoughtUserCount, sharesSold, sharesSoldUserCount);
}
}
class CamelCaseNamingStrategy extends PropertyNamingStrategy
{
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
{
return convert(defaultName);
}
private String convert(String defaultName)
{
char[] nameChars = defaultName.toCharArray();
StringBuilder nameTranslated = new StringBuilder(nameChars.length * 2);
for (char c : nameChars)
{
if (Character.isUpperCase(c))
{
nameTranslated.append("_");
c = Character.toLowerCase(c);
}
nameTranslated.append(c);
}
return nameTranslated.toString();
}
}

Categories

Resources