GSON deserialize/serialize a json string that has undetermined names of keys - java

I have JSON like this:
# "trig_cond": {
# "_and": {
# "param1": ["op", "value1"],
# "param2": ["op", "value2"], ...
# },
# "_or": {
# "param1": ["op", "value1"],
# "param2": ["op", "value2"], ...
# }
# }
the "_and"/"_or" part has an undetermined number of keys that map to list objects (each containing two items).
How can I deserialize this into a java object? I've looked at various custom deserialization options but I don't understand how to do it for this example json.

There are few different options you can take depending on your requirements. One of the simplest solutions you can take when you have an unknown number of keys is to simply leave these parts of your POJO as JSONObject.
For example, this POJO would meet your requirements to deserialize the JSON into a java object, and still provide the flexibility you are after.
public class SamplePojo {
TrigCond trig_cond;
public static class TrigCond {
JsonObject _and;
JsonObject _or;
}
}
You could also split them into different files if you prefer.
Also, since in your example, you know what types of values are contained with these param1 and param2 array elements, you could instead using a Map
Here is another example which you may prefer:
public class SamplePojo {
TrigCond trig_cond;
public static class TrigCond {
Map<String, String[]> _and;
Map<String, String[]> _or;
}
}
Let me know if I understood your question correctly!

Related

Parsing dynamic JSON values to Java objects

In my application I have lot of overviews (tables) with sorting and filtering capabilities. And becuase the different column can hold different value type (strings, numbers, dates, sets, etc.) the filter for these columns also can bring different values. Let me show you few examples (converted to JSON already as is sent to server via REST request):
For simple string value it is like:
{"<column_name>":"<value>"}
For number and date column the filter looks like:
{"<column_name>":[{"operator":"eq","value":"<value>"}]}
{"<column_name>":[{"operator":"eq","value":"<value1>"},{"operator":"gt","value":"<value2>"}]}
For set the filter looks like
{"<column_name>":["<value1>","<value2>"(,...)]}
Now I need to parse that JSON within a helper class that will build the WHERE clause of SQL query. In PHP this is not a problem as I can call json_decode and then simply check whether some value is array, string or whatever else... But how to do this simply in Java?
So far I am using Spring's JsonJsonParser (I didn't find any visible difference between different parsers coming with Spring like Jackson, Gson and others).
I was thinking about creating an own data object class with three different constructors or having three data object classes for all of the three possibilities, but yet I have no clue how to deal with the value returned for column_name after the JSON is parsed by parser...
Simply looking on the examples it gives me three possibilities:
Map<String, String>
Map<String, Map<String, String>>
Map<String, String[]>
Any idea or clue?
Jackson's ObjectMapper treeToValue should be able to help you.
http://fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/fasterxml/jackson/databind/ObjectMapper.html#treeToValue%28com.fasterxml.jackson.core.TreeNode,%20java.lang.Class%29
Your main problem is that the first version of you JSON is not the same construction than the two others. Picking the two others you could deserialize your JSON into a Map<String, Map<String, String> as you said but the first version fits a Map.
There are a couple solutions available to you :
You change the JSON format to always match the Map<String, Map<String, String> pattern
You first parse the JSON into a JsonNode, check the type of the value and deserialize the whole thing into the proper Map pattern.
(quick and dirty) You don't change the JSON, but you try with one of the Map patterns, catch JsonProcessingException, then retry with the other Map pattern
You'll have to check the type of the values in runtime. You can work with a Map<String, Object> or with JsonNode.
Map<String, Object>
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(str);
Object filterValue = filter.get("<column_name>");
if (filterValue instanceof String) {
// str is like "{\"<column_name>\":\"<value>\"}"
} else if (filterValue instanceof Collection) {
for (Object arrayValue : (Collection<Object>) filterValue) {
if (arrayValue instanceof String) {
// str is like "{\"<column_name>\":[\"<value1>\",\"<value2>\"]}"
} else if (arrayValue instanceof Map) {
// str is like "{\"<column_name>\":[{\"operator\":\"eq\",\"value\":\"<value>\"}]}"
}
}
}
JsonNode
ObjectMapper mapper = new ObjectMapper();
JsonNode filter = mapper.readTree(str);
JsonNode filterValue = filter.get("<column_name>");
if (filterValue.isTextual()) {
// str is like "{\"<column_name>\":\"<value>\"}"
} else if (filterValue.isArray()) {
for (JsonNode arrayValue : filterValue.elements()) {
if (arrayValue.isTextual()) {
// str is like "{\"<column_name>\":[\"<value1>\",\"<value2>\"]}"
} else if (arrayValue.isObject()) {
// str is like "{\"<column_name>\":[{\"operator\":\"eq\",\"value\":\"<value>\"}]}"
}
}
}

Associativity array in Java

I receive a List<org.apache.avro.generic.GenericRecord> with the data contents as shown below (JSON notation used for clarity). How can I best hold these record types using Java?
Record 1:
[
{
"serial_no" : "x",
"data1" : "d"
},
{
"serial_no" : "y",
"data2" : "d2"
},
............................MANY MORE
]
Record 2:
[
{
"id":"x",
"type":"A"
},
{
"id" : "x",
"type" : "B"
},
{
"id" : "y",
"type" : "A",
},
{
"id" : "y",
"type" : "B"
}
]
As you see here, each serial number has two records in record2. serial_no in record1 is same as id in record2.
My Goal is:
Fatsest way to find these two records.
Solution I think:
Create a map like
map.put("x", [map.put("A",List), map.put("B",List)]);
But I feel like, its a complex structure. Because map contains list of maps[each map is Map<String,List<Map<String,String>>>].
Any suggestions?
EDIT
Each entries in records are avro GenericRecord
It looks as if you are trying to parse JSON using Java. Why not use a specific library for that?
Like the basic http://www.json.org/java/ or Google's https://github.com/google/gson
Otherwise, I do not think that the complex structure you are proposing is especially slow. You might want to design your own object class to hold the data if you think it is more efficient or easier to get to the data.
EDIT
Based on your question I assumed JSON was the format you received it in, sorry.
I would just create a wrapper for GenericRecord, or subclass it. Then add the methods that you need to extract the data, or make it Comparable for sorting.
Something along the lines of
public class MyRecord extends GenericRecord implements Comparable<MyRecord>
{
// Determine the type
public int getType()
{
if ( this.get( "id") != null )
return 2;
return 1;
}
// Add methods that allow you to retrieve the serial field from any of the two record types
public String getId()
{
if ( this.get( "id") != null )
return (String)this.get("id");
return (String)this.get("serial_no");
}
// add comparator methods that will allow you to sort the list, compare based on Id, etc
#Override
public int compareTo(MyRecord another)
{
// Just a simple example
return this.getId().compareTo( another.getId() );
}
}
Define classes for repeated entries:
class SerialNoData {
String serialNo;
Object data;
}
and
class IdType {
String id;
String type;
}
; once parsed put the instances into arrays or Lists to get the desired format.
How complex the map is doesn't really make a difference for the speed. Depending on the type of Map you use getting a list of records will be constant time (with a reasonably small overhead). Finding something in the sublists will then be O(n), since you need to iterate through the list and look at all the Maps.
Define following classes
class Serial{
String serial-no;
String data;
List<IdType> idTypes;
}
class IdType{
String id;
String type;
}
After that you can use jackson or any kind of JSON processing library.

Inverted order of JSON elements in Java after XML conversion

I'm using the JSON in Java for the transformation of XML to JSON. I have the problem that this implementation is inverting all child elements.
When I pass this XML:
<Person><Child1>a</Child1><Child2>b</Child2></Person>
I will end up with a JSON having the childs inverted:
{"Person":{"Child2":"b", "Child1":"a"}}
My Java code:
JSONObject jsonObject= XML.toJSONObject("<Person><Child1>a</Child1><Child2>b</Child2></Person>");
String myJSONString = jsonObject.toString(4);
How to transform to JSON with keeping the order of the elements (like in XML)?
So my question. How to transform to JSON with keeping the order?
With the current official JSONObject, this is not possible. The API makes it very clear:
A JSONObject is an unordered collection of name/value pairs.
But, there might be a quick workaround for your problem. As from what I've investigated in the JSONObject source code, you can see that it uses a HashMap internally, and as you know HashMap doesn't keep any order.
public JSONObject() {
this.map = new HashMap<String, Object>();
}
You have 2 alternatives:
Modify the current JSONObject source code so that the map is initialized with a LinkedHashMap. A LinkedHashMap is an implementation of the Map interface, with predictable iteration order:
public JSONObject() {
this.map = new LinkedHashMap<String, Object>();
}
Make your own custom class which extends JSONObject but uses a LinkedHashMap internally. Notice that you still have to make some changes in JSONObject.
public class JSONObject {
//private final Map<String,Object> map; // current approach
//you have to remove final modifier and either add a getter or make it protected. I'll choose the change modifier to protected in this case.
protected Map<String,Object> map;
}
public class JSONObjectOrdered extends JSONObject {
public JSONObjectOrdered(){
this.map = new LinkedHashMap <String, Object>();
}
}
As JSONObject is an unordered collection of name/value pairs, no choice, you have to use a JSONArray.
Here is my solution, modify the XML class, particularly the method parse, in order to return JSONArray for storing child nodes.
My modified class : XML.java
XML input
<Person name="test"><Child1>a</Child1><Child2>b</Child2><Child3></Child3></Person>
Usage :
JSONObject jsonObject= XML.toJSONObject("<Person name=\"test\"><Child1>a</Child1><Child2>b</Child2><Child3></Child3></Person>");
System.out.println(jsonObject);
Out :
{"Person":{"CHILDREN":[{"Child1":"a"},{"Child2":"b"},{"Child3":""}],"name":"test"}}
Conclusion
The children order is kept. Off course this idea can be improved, it's just a POC regarding what can be done, modifying the parser.
JSON objects don't have a specific order. You can of course change the serialization implementation to keep an order but there is no guarantee that it is also kept after deserialization. In fact, most JSON libraries won't even have an API to detect in which order the original JSON text was parsed. You shouldn't care about ordering when using objects.
If you do care about the order though, use a JSON array.
{"Person":[{"Child1":"a"},{"Child2":"b"}]}
The JSONObject API dose not guarantee the elements order
A nice solution to this issue can be using JSONArray, in JSONArray the order you insert the elements is saved.
So, in your case you will have an array of "chides" for each person.
you would probably will need to change the XML file or manually parse the XML into the json in your format (the JSONArray instead of what you are using now)
If you are hell bent on getting the output ordered the way you want it you could always try overriding the method
toString()
You can download the source code from http://www.json.org/java/ and modify JSONObject.java using TreeMap instead of HashMap.
You also can override method in JSONObject.java
public Iterator<String> keys() {
return this.keySet().iterator();
}
Make sure the Iterator is the one of the sorted keys.
If you would use Jackson for JSON serialization / deserialization you could simply put a
#JsonPropertyOrder() annotation on top of your class.
#JsonPropertyOrder({"Child1", "Child2"})
public class Person {
#JsonProperty("Child1")
public String child1;
#JsonProperty("Child2")
public String child2;
}
You can keep order of incoming data when modify
private final Map<String, Object> nameValuePairs;
/**
* Creates a {#code JSONObject} with no name/value mappings.
*/
public JSONObject() {
nameValuePairs = new HashMap<String, Object>();
}
to
private final Map<String, Object> nameValuePairs;
/**
* Creates a {#code JSONObject} with no name/value mappings.
*/
public JSONObject() {
nameValuePairs = new LinkedHashMap<String, Object>();
}
Because instead of HashMap - LinkedHashMap have an predictable iteration order.
LinkedHashMap : Hash table and linked list implementation of the Map interface, with predictable iteration order.
So is the most effective way to resolve your problem.
And also you can fork to use a custom library from
https://github.com/SergeyShustikov/JSON-java

Best way to save some data and then retrieve it

I have a project where I save some data coming from different channels of a Soap Service, for example:
String_Value Long_timestamp Double_value String_value String_value Int_value
I can have many lines (i.e. 200), with different values, like the one above.
I thought that I could use an ArrayList, however data can have a different structure than the one above, so an ArrayList maybe isn't a good solution in order to retrieve data from it.
For example above I have, after the first two values that are always fixed, 4 values, but in another channel I may have 3, or 5, values. What I want retrieve data, I must know how many values have a particular line, and I think that Arraylist doesn't help me.
What solution could I use?
When you have a need to uniquely identify varying length input, a HashMap usually works quite well. For example, you can have a class:
public class Record
{
private HashMap<String, String> values;
public Record()
{
// create your hashmap.
values = new HashMap<String, String>();
}
public String getData(String key)
{
return values.get(key);
}
public void addData(String key, String value)
{
values.put(key, value);
}
}
With this type of structure, you can save as many different values as you want. What I would do is loop through each value passed from Soap and simply add to the Record, then keep a list of Record objects.
Record rec = new Record();
rec.addData("timestamp", timestamp);
rec.addData("Value", value);
rec.addData("Plans for world domination", dominationPlans);
You could build your classes representing the entities and then build a parser ... If it isn't in a standard format (eg JSON, YAML, ecc...) you have no choice to develop your own parser .
Create a class with fields.
class ClassName{
int numberOfValues;
String dataString;
...
}
Now create an ArrayList of that class like ArrayList<ClassName> and for each record fill that class object with numberOfValues and dataString and add in Arraylist.

Parsing dynamic JSON data with Gson

I'm a beginner Java and Gson user and have been able to apply it to my needs. I now have some JSON data that I need to parse into a spinner as follows:
{
"lang":[
"arabic",
"bengali",
"dutch-utf8",
"eng_root",
"english",
"english-utf8",
...
],
"themes":{
"blue":{
"chinese_ibm500":1,
"spanish":1,
"bengali":1,
"japanese":1,
"english":1,
"russian":1,
"french-utf8":1,
"eng_root":1,
"arabic":1,
"spanish-utf8":1,
"portuguese":1,
...
},
"green":{
"eng_root":1,
"engmonsoon":1,
"english":1
...
},
"red":{
"chinese_ibm500":1,
"spanish":1,
"bengali":1,
...
}
}
}
So from this JSON I need 2 things:
1) the array under lang is dynamic as for its the languages installed on the server. How could I get all the entries?
I have a class as follows but im stuck as to what I should do after I return lang
public class ListData {
private List<Language> lang;
public List<Language> getLang {
return lang;
}
public static class Language {
???
}
}
2) after understanding 1 I might be able to figure this one out. Under themes are colors which again can be more or less {purple, orange, whatever}. I just need a list of those themes, as far as I'm concerned I don't need to know the languages for each.
Feel like this question is turning into a book. I have searched SO extensively and hate asking questions but I'm pretty stumped. Thanks in advance.
1) In order to get the "lang" array, just modify
private List<Language> lang;
for
private List<String> lang;
Since the elements inside "lang" array are all strings, you don't need any class Language to store those values, they'll be parsed correctly as strings. And it doesn't matter how many strings the array contains...
2) In order to parse "themes", you have to notice that it's not an array [ ], but an object { }, so you do need to parse it with some object, and the most suitable class here is a Map like this:
private Map<String, Object> themes;
Note: as you said that you don't need the data under "blue", "green", etc... you can just Object as the value type in the map, otherwise you'd need some class...
Using a Map here allows you to have an arbitrary number of themes in your JSON response.
So in summary, you just need a class like:
public class ListData {
private List<String> lang;
private Map<String, Object> themes;
//getters & setters
}
and parse your JSON with:
Gson gson = new Gson();
ListData data = gson.fromJson(yourJsonString, ListData.class);
Your list of langs will be under:
data.getLang();
and your list of themes will be under:
data.getThemes().keySet();
I suggest you to take a look at Gson documentation. It's quite short and clear and you'll understand everything much better...

Categories