Jackson ObjectMapper with arbitrary JSON keys - java

I'm using Jackson 1.9.5 in an Android project to parse JSON files.
So far I haven't had any problems, and can parse files fine using the following code:
AssetManager mgr = getAssets();
ObjectMapper mapper = new ObjectMapper();
try {
InputStream ifp = mgr.open("detail_schema.json");
schema = mapper.readValue(ifp, DetailSchema.class);
} catch (IOException e) {
e.printStackTrace();
}
Where the DetailSchema class consists of a mix of primitive types and classes. I'm now running into a problem where I want to parse some JSON like the following:
"fields": {
"Suburb": "Paddington",
"State": "NSW",
"Post Code": "2074",
"Lollipop": "Foo Bar Haz"
}
Where I can't possibly know the map keys before hand (they can be user-defined). As such, I'm not sure what the associated Java class should look like.
Ie, for this example, it could look like:
public class MyClass {
public String Suburb;
public String State;
public String PostCode;
public String Lollipop;
}
But this may not be correct for another instance of the JSON file. Ideally I need some way for Jackson to map values to something like a NameValuePair. I suspect that the automatic object mapping may not be an option in this case - can someone confirm or deny this?

You have two options. Either you can use readTree in ObjectMapper, which returns a JsonNode. Working with a JsonNode is much like working with a tree, so you can get children nodes, read values, et cetera:
InputStream ifp = mgr.open("detail_schema.json");
JsonNode root = mapper.readTree(ifp);
JsonNode fields = root.get("fields");
for (JsonNode children : fields) {
// ...
}
Then you'd need to build your DetailSchema object manually.
Or, you can let Jackson deserialize it as a Map, in which case you'd use your code but where MyClass would be like this:
public class MyClass {
public Map<String, Object> fields;
// getter/setters
}
You can probably type the map values as String as well if you are sure the inputs are text in json. (Actually, I'm not sure what type enforcement Jackson does, maybe it will allow anything anyway...)

Related

Create an universal java mapper for json string

I'm working on a Spring-boot project where I receive different format of Json String. My goal is to convert these Json string into an Unified Java class.
I can receive many variations of this Json:
{ "id" : "someId", "type" : "temperature", "value" : 21.0 }
For example, one variation might look like :
{ "id" : "someId", "data" : { "type": "temp", "val" : 21.0 }, "location": "here" }
So these 2 Json must be mapped into the same Java class.
I already have 2 solutions in mind :
First solution
1) Create a Specific Java Class for each Json that I may receive
2) Create a function that takes this specific object and return the Unified Java Class
Second solution
1) Create a JsonNode with the Json String
2) For each key try to match it with a field of the Unified Java Class.
But we have to take into consideration every key possible of a node like "value" or "val".
What is the best approach to solve this problem ?
I'm looking for a solution that could be easy to maintain.
Edit : I'm already using Jackson, but my problem is to map this Json object into an universal Java Class independently of the Json
Edit 2 : The Unified Java Class is a class model that already exist and it's used to store information in our database. So to push information inside our database, I have to convert each json I receive into this unified format
I can see following solutions. E.g. you use Jackson for parse JSON you could declare you custom ObjectMapper:
ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
This mapper contains additional options to ignore unknow properties.
Do you Map<String, Object> as destination class. This is magic key and it works always. Contra: you do not have json validation and have to add many constant keys to read this.
Example:
public static <T> Map<String, T> readMap(String json) throws NGPException {
if (json == null) {
return null;
}
ObjectReader reader = JSON_MAPPER.readerFor(Map.class);
MappingIterator<Map<String, T>> it = reader.readValues(json);
if (it.hasNextValue()) {
Map<String, T> res = it.next();
return res.isEmpty() ? Collections.emptyMap() : res;
}
return Collections.emptyMap();
}
Client:
Map<String, Object> map = readMap("json string");
String id = (String)map.getOrDefault("id", null);
Second way is to build one general class that contain all posiible variables. Additionnaly you have to set option to Jackson ignore unknown fields. In this case, existed fields will be used by Jackson.
Example:
public static <T> T read(String json, Class<T> clazz) throws NGPException {
return mapper.readerFor(clazz).readValue(json);
}
class Response {
private String id;
private String type;
private Double value;
private String location;
private Data data;
public class Data {
private String type;
private String temp;
private Double value;
}
}
Client:
Response response = read("json string", Response.class);
I usually use GSon from Google. It is really usefull. Check gson.fromJson(yourJsonString) in your case.
You can easy use
Gson gson = new Gson();
Data data = gson.fromJson(jsonString, Data.class);

Deserializing multiple fields in Jackson

Suppose I have JSON that looks something like this:
{ "key1":1, "key2":2, "key3":3 }
Where the number of "key-n" fields is unknown, but are consecutively numbered starting at 1. I wish to deserialize it into an object as follows:
public class MyPojo {
private List<Integer> keys;
}
That is, keys.get(0) corresponds to the key1 field, and so on. The JSON may have other non-"key-n" fields as well.
I had been under the impression that something like
public class MyPojo {
#JsonUnwrapped #JsonDeserialize(using = KeyDeserializer.class) private List<Integer> keys;
}
where KeyDeserializer is a JsonDeserializer would just extract all of the "key-n" fields, would work; however, I had discovered that the deserializer isn't being invoked because the JSON lacks a field named key.
Since the JSON is third-party, I can't really try to modify the JSON, so I am wondering if there are any alternate approaches to this problem.

How to check json for key and if it exists then set the value in java model Object

I have a model object which is initialized with default values. To refresh the content of object I call an web service and get the response and get the content from json object.
I want to check If json response contains the object or not. If it does then call the setter and set the data and if it doesn't then leave then don't set it. I have approx 300 fields in my object. How I can do it with less code. I am listing my current approach.
My Model object is like
public class MyObject {
private String str1 = "Initial Value1";
private String str2 = "Initial Value2";
public void setStr1(String str1)
{
this.str1 = str1;
}
public void setStr2(String str2)
{
this.str2 = str2;
}
public String getStr1(){
return str1;
}
public String getStr2(){
return str2;
}
}
my json response be like
{
"val_one":"New Value1",
"val_two":"New_value2"
}
Now at run time I need to set the value from json response
MyObject myObject = new MyObject();
if(jsonObject.has("val_one"));
myObject.setStr1(jsonObject.get("val_one"));
if(jsonObject.has("val_two"));
myObject.setStr2(jsonObject.get("val_two"));
Now how to do it in a better and efficient
If both sides are using JAVA then why not just use json-io. You can create an object as normal. ie
Animal a = new Aminmal() andimal.setName("bob");
Then use json-io to make it into json -- stream to where ever it needs to be... use json io to change back to object
This can be done using
JsonWriter.objectToJson(Object o);
JsonReader.jsonToJava(String json);
https://code.google.com/p/json-io/
json-io is also extremely light weight and quicker than most if not all other third party json library's that I have used.
That being said if you want to have more control on the output ie.. date conversions etc.. then look at GSON.
https://code.google.com/p/google-gson/
Another option, in addition to the other suggestions is gson. Here the link for gson information.
Essentially the idea with gson being that you define an object to represent the JSON structure that you are receiving. So somewhat like what you have now, you'd just need to change the object attributes to match the names of the JSON fields, ie 'val_one' and 'val_two'.
Then you just need to use gson to create the object from the JSON text, eg:
Gson gson = new GsonBuilder().create();
MyObject json = gson.fromJson(jsonStr, MyObject.class);
Why do you want to take of the object model mapping yourself? If you take spring then you can use the jackson mapper and have it all done for you.
If you don't want to use spring then you still can use jackson2 and let it handle the parsing:
http://wiki.fasterxml.com/JacksonRelease20

Map generic JSON object to Map interface with GSON

Given the following JSON object
{
"id": 5,
"data: { ... }
}
Is it possible to map this to the following POJO?
class MyEntity {
int id;
Map<String, Object> data;
}
Because I would like to leave the data object open ended. Is this even possible or what is a better approach to go about this? I am doing this on Android.
I don't have any idea about Android application but you can achieve it using Gson library easily.
The JSON that is used in your post is not valid. It might be a typo. Please validate it here on JSONLint - The JSON Validator
Simply use Gson#fromJson(String, Class) method to convert a JSON string into the object of passed class type.
Remember the name of instance member must be exactly same (case-sensitive) as defined in JSON string as well. Read more about JSON Field Naming
Use GsonBuilder#setPrettyPrinting() that configures Gson to output Json that fits in a page for pretty printing.
Sample code:
String json = "{\"id\": 5,\"data\": {}}";
MyEntity myEntity = new Gson().fromJson(json, MyEntity.class);
String prettyJsonString = new GsonBuilder().setPrettyPrinting().create().toJson(myEntity);
System.out.println(prettyJsonString);
output:
{
"id": 5,
"data": {}
}

Gson parse unquoted value

I using in my project GSON library, everything is fine, but now i'm stuck with a problem, where i need to use a custom deserializer on unquoted values.
I have the following value, and need to parse from json:
[ ["county","=", field_name], ["name", "ilike", "username"] ]
I need to parse unquoted values with a custom deserializer, to a wrapper class like:
public class StringField {
private String value;
public String getValue() {
return value;
}
}
And value will have "field_name" as string.
The problem is that the data is not valid JSON.
JSON does not permit such "unquoted value" strings such as field_name and neither does Gson. Either fix the input such that it is valid JSON (perhaps "$field_name$") - or use a tool (i.e. not Gson) that can cope with non-JSON text that resembles JSON.
This situation can't be corrected with Custom Deserialization because the data isn't even parsed correctly to Json tokens: Gson will throw an exception as the invalid/non-JSON is encountered.
At the very least this would require creating a customized JsonReader implementation that can read "barewords" as strings. However, this is problematic to do because JsonReader does not conform to any specialized interfaces (so it must be subclassed, oops!) and is final (so it can't be subclassed, oops!). As such, unless willing to edit the Gson library source: not possible.
With the below code, I parsed your JSON without problems, I left Gson decide how to parse it, except assuming it contained a List outermost. And the result was a List of Lists of Strings. I did not understand very well why you need StringField class.
package stackoverflow.questions;
import java.util.List;
import com.google.gson.*;
public class Q20557131 {
public static void main(String[] args){
String json = "[[\"county\",\"=\", field_name], [\"name\", \"ilike\", \"username\"]]";
Gson g = new Gson();
List outerList = g.fromJson(json, List.class);
List innerList = (List) outerList.get(0);
for(Object o: innerList)
System.out.println(o.getClass());
}
}
By default, Gson 2.2.4 is lenient, even if has the lenient property set to false, from documentation
Configure this parser to be be liberal in what it accepts. By default, this parser is strict and only accepts JSON as specified by RFC 4627. Setting the parser to lenient causes it to ignore the following syntax errors:
....
Strings that are unquoted or 'single quoted'.
...
even if documentation states that property is false by default, in the source code of the JsonReader#fromJson:
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true); <-- always true
try {
reader.peek();
isEmpty = false;
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
} catch (EOFException e) {
...
I've solved this problem years ago in another way (sorry for delayed). Wrote symbolic preprocessor class, which replace by regexp labels like field_name with actual values from model and then parsed json.

Categories

Resources