Mapping between JSON formats in Java - java

I'm coming to Java from JavaScript/Ruby. Let's say I've got the following JSON object for an animal:
{
name: {
common: "Tiger",
latin: "Panthera tigris"
}
legs: 4
}
I'm dealing with lots of animal APIs, and I want to normalize them all into my own common format, like:
{
common_name: "Tiger",
latin_name: "Panthera tigris",
limbs: {
legs: 4,
arms: 0
}
}
In, say, JavaScript, this would be straightforward:
normalizeAnimal = function(original){
return {
common_name: original.name.common,
latin_name: original.name.latin,
limbs: {
legs: original.legs || 0,
arms: original.arms || 0
}
}
}
But what about in Java? Using the JSONObject class from org.json, I could go down the road of doing something like this:
public JSONObject normalizeAnimal(JSONObject original) throws JSONException{
JSONObject name = original.getJSONObject("name");
JSONObject limbs = new JSONObject();
JSONObject normalized = new JSONObject();
normalized.put("name_name", name.get("common"));
normalized.put("latin_name", name.get("latin"));
try{
limbs.put("legs", original.get("legs");
}catch(e){
limbs.put("legs", 0);
};
try{
limbs.put("arms", original.get("arms");
}catch(e){
limbs.put("arms", 0);
};
normalized.put("limbs", limbs);
return normalized;
}
This gets worse as the JSON objects I'm dealing with get longer and deeper. In addition to all of this, I'm dealing with many providers for animal objects and I'll eventually be looking to have some succinct configuration format for describing the transformations (like, maybe, "common_name": "name.common", "limbs.legs": "legs").
How would I go about making this suck less in Java?

Use a library like Gson or Jackson and map the JSON to a Java Object.
So you're going to have a bean like
public class JsonAnima {
private JsonName name;
private int legs;
}
public class JsonName {
private String commonName;
private String latinName;
}
which can be easily converted with any library with something like (with Jackson)
ObjectMapper mapper = new ObjectMapper();
JsonAnimal animal = mapper.readValue(jsonString, JsonAnimal.class);
then you can create a "converter" to map the JsonAnimal to you Animal class.
This can be a way of doing it. : )
Some links:
Gson: http://code.google.com/p/google-gson/
Jackson: http://wiki.fasterxml.com/JacksonHome

The pure Java solutions all are challenged to deal with unreliable structure of your source data. If you're running in a JVM, I recommend that you consider using Groovy to do the Parse and the Build of your source JSON. The result ends up looking a lot like the Javascript solution you outlined above:
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
def originals = [
'{ "name": { "common": "Tiger", "latin": "Panthera tigris" }, "legs": 4 }',
'{ "name": { "common": "Gecko", "latin": "Gek-onero" }, "legs": 4, "arms": 0 }',
'{ "name": { "common": "Liger" }, "legs": 4, "wings": 2 }',
'{ "name": { "common": "Human", "latin": "Homo Sapien" }, "legs": 2, "arms": 2 }'
]
originals.each { orig ->
def slurper = new JsonSlurper()
def parsed = slurper.parseText( orig )
def builder = new JsonBuilder()
// This builder looks a lot like the Javascript solution, no?
builder {
common_name parsed.name.common
latin_name parsed.name.latin
limbs {
legs parsed.legs ?: 0
arms parsed.arms ?: 0
}
}
def normalized = builder.toString()
println "$normalized"
}
Running the script above deals with "jagged" JSON (not all elements have the same attributes) and outputs like...
{"common_name":"Tiger","latin_name":"Panthera tigris","limbs":{"legs":4,"arms":0}}
{"common_name":"Gecko","latin_name":"Gek-onero","limbs":{"legs":4,"arms":0}}
{"common_name":"Liger","latin_name":null,"limbs":{"legs":4,"arms":0}}
{"common_name":"Human","latin_name":"Homo Sapien","limbs":{"legs":2,"arms":2}}

If you'll be using this for many different types of objects, I would suggest to use reflection instead of serializing each object manually. By using reflection you will not need to create methods like normalizeAnimal, you just create one method or one class to do the serialization to json format.
If you search for "mapping json java" you'll find some useful references. Like gson. Here is an example that is on their website:
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
//(Serialization)
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
///==> json is {"value1":1,"value2":"abc"}
///Note that you can not serialize objects with circular references since that will result in infinite recursion.
//(Deserialization)
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
//==> obj2 is just like obj

You can try little jmom java library
JsonValue json = JsonParser.parse(stringvariablewithjsoninside);
Jmom mom = Jmom.instance()
.copy("/name/common", "/common_name", true)
.copy("/name/latin", "/latin_name", true)
.copy("/arms", "/limbs/arms", true)
.copy("/legs", "/limbs/legs", true)
.remove("/name")
;
mom.apply(json);
String str = json.toPrettyString(" ");

Related

Simplest way to convert this array in to the specified one using Java

I have this String Json Payload
[
"key1":{
"atr1":"key1",
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
"key2":{
"atr1":"key2",
"atr2":"value5",
"atr3":"value6",
"atr4":value7,
"atr5":"value8"
}
]
and I want it to be converted in to the following format using Java
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4": "value7",
"atr5":"value8"
}
]
What would be the simplest way of transforming this ?
You cannot, because the example below is not valid json.
Check it out using this JSON validator.
If you paste this in (I've fixed some basic errors with lack of quotes)
{
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
}
You will get these errors ...
It can work if you change the target schema to something like this by using a json-array to contain your data.
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
]
If this works for you, then this problem can easily be solved by using the ObjectMapper class.
You use it to deserealize the original JSON into a class, which has two fields "key1" and "key2"
Extract the values of these fields and then just store them in an array ...
Serialize the array using the ObjectMapper.
Here a link, which explains how to use the ObjectMapper class to achieve the goals above.
EDIT:
So you'll need the following classes to solve the problem ...
Stores the object data
class MyClass {
String atr2;
String art3;
}
Then you have a container class, which is used to store the initial json.
class MyClassContainer {
MyClass key1;
MyClass key2;
}
Here's how you do the parse from the original json to MyClassContainer
var mapper = new ObjectMapper()
var json = //Get the json String somehow
var myClassContainer = mapper.readValue(json,MyClassContainer.class)
var mc1 = myClassContainer.getKey1();
var mc2 = myClassContainer.getKey2();
var myArray = {key1, key2}
var resultJson = mapper.writeValueAsString(myArray)
Assuming that you will correct the JSON into a valid one (which involves replacing the surrounding square braces with curly ones, and correct enclosure of attribute values within quotes), here's a simpler way which involves only a few lines of core logic.
try{
ObjectMapper mapper = new ObjectMapper();
mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
HashMap<String, Data> map = mapper.readValue( jsonString, new TypeReference<HashMap<String, Data>>(){} );
String json = mapper.writeValueAsString( map.values() );
System.out.println( json );
}
catch( JsonProcessingException e ){
e.printStackTrace();
}
jsonString above is your original JSON corrected and valid JSON input.
Also notice the setting of FAIL_ON_UNKNOWN_PROPERTIES to false to allow atr1 to be ignored while deserializing into Data.
Since we are completely throwing away attr1 and its value, the Data class will represent all fields apart from that.
private static class Data{
private String atr2;
private String atr3;
private String atr4;
private String atr5;
}

how to update the json with map values using java?

I have below json:
{
"type":Flowers,
"input_data": [
{
"id": 35742,
"Request_ID": 8383,
"data_line": "*****Sample text here*****",
"variety": {
"Rose": 0,
"Jasmine": 0,
"Lily": 1,
"Sunflower": 1,
},
"responded": 1
},
{
"id": 35992,
"Request_ID": 8983,
"data_line": "*****Sample text here*****",
"variety": {
"Rose": 1,
"Jasmine": 0,
"Lily": 0,
"Sunflower": 1,
},
"responded": 1
}
],
"token": "F9500930C-15A6-4111-AD7F-7D0DF0CEE4D8"
}
How do I map the values in "variety" with "id"?
Note: id is coming from the response of a different API which should be replaced in this json and mapped with variety.
It's not a good idea to have a field like 'input_data' in your json.
Try to redo your json that's aligned to your data model and something that can be mapped to Java objects.
GSON is a great library for dealing with JSONs in Java - https://github.com/google/gson
I can give you a utility method to get a list of Java objects from a json reprenstation like this:
private static Type typeOfT = TypeToken.getParameterized(List.class, <<your-class>>.class).getType();
public static <T> List<T> loadListFromFile(String fileName, Class<T> clazz) throws Exception{
File file = new File(fileName);
String json = new String(Files.readAllBytes(file.toPath()));
return gson().fromJson(json, typeOfT);
}
First, your JSON string is invalid (e.g. Flowers should be quoted by double quotes). Second, I am confused about what you really want. Therefore, according to the comment under your question, you said that you need a mapping between id and variety. Here comes one way to achieve that by following steps with Jackson (One of the most popular JSON libraries.).
Step 1
Create nested POJOs for deserialization.
class Result {
#JsonProperty("input_data")
private List<InputData> inputData;
//general getter and setter
}
class InputData {
private int id;
private Variety variety;
//general getters and setters
}
class Variety {
#JsonProperty("Rose")
private int rose;
#JsonProperty("Jasmine")
private int jasmine;
#JsonProperty("Lily")
private int lily;
#JsonProperty("Sunflower")
private int sunflower;
//general getters, setters and toString()
}
Step 2
Deserialize JSON response to Result and transform the InputData into Map<Integer, Variety> with Java 8.
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Result result = mapper.readValue(jsonStr, Result.class);
Map<Integer, Variety> idVarietyMap = result.getInputData().stream()
.collect(Collectors.toMap(InputData::getId, InputData::getVariety));
System.out.println(idVarietyMap.toString());
Console output
{35992=Variety [rose=1, jasmine=0, lily=0, sunflower=1], 35742=Variety [rose=0, jasmine=0, lily=1, sunflower=1]}

Parsing JSON into Jackson using a stream/object approach

I have a JSON file which can have multiple types.
For example:
{
"dog": {
"owner" : "John Smith",
"name" : "Rex",
"toys" : {
"chewtoy" : "5",
"bone" : "1"
}
},
"person": {
"name" : "John Doe",
"address" : "23 Somewhere Lane"
}
// Further examples of dogs and people, and a few other types.
}
I want to parse these into objects. ie. I want to create a Dog object with owner/name/toys attributes, and person with name/address attributes, and use Jackson to read through and create objects out of them.
The ordering matters - I need to know that Rex came before John Doe, for example. I would prefer to do with a stream like approach (ie. read and parse Rex into the Dog object, do something with it, then discard it, then move onto to John Doe). So I need a stream based approach.
I can't figure out how to use both the stream reading API (to go through in order) and the ObjectMapper interface (in order to create Java objects out of JSON) to accomplish this.
To do this, you need to use an object mapper with your factory
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
...
private static ObjectMapper mapper = new ObjectMapper();
private static JsonFactory factory = mapper.getJsonFactory();
Then create a parser for the input.
JsonParser parser = factory.createJsonParser(in);
Now you can mix calls to parser.nextToken() and calls to parser.readValueAs(Class c). Here is an example that gets the classes from a map:
Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
classMap.put("dog", Dog.class);
classMap.put("person", Person.class);
InputStream in = null;
JsonParser parser = null;
List<Object> results = new ArrayList<Object>();
try {
in = this.getClass().getResourceAsStream("input.json");
parser = factory.createJsonParser(in);
parser.nextToken();// JsonToken.START_OBJECT
JsonToken token = null;
while( (token = parser.nextToken()) == JsonToken.FIELD_NAME ) {
String name = parser.getText();
parser.nextToken(); // JsonToken.START_OBJECT
results.add(parser.readValueAs(classMap.get(name)));
}
// ASSERT: token = JsonToken.END_OBJECT
}
finally {
IOUtils.closeQuietly(in);
try {
parser.close();
}
catch( Exception e ) {}
}

Read part of a JSON String using Jackson

The JSON string is as follows
{
"rank":"-text_relevance",
"match-expr":"(label 'star wars')",
"hits":{
"found":7,
"start":0,
"hit":[
{"id":"tt1185834",
"data":{
"actor":["Abercrombie, Ian","Baker, Dee","Burton, Corey"],
"title":["Star Wars: The Clone Wars"]
}
},
.
.
.
{"id":"tt0121766",
"data":{
"actor":["Bai, Ling","Bryant, Gene","Castle-Hughes, Keisha"],
"title":["Star Wars: Episode III - Revenge of the Sith"]
}
}
]
},
"info":{
"rid":"b7c167f6c2da6d93531b9a7b314ad030b3a74803b4b7797edb905ba5a6a08",
"time-ms":2,
"cpu-time-ms":0
}
}
It has many fields, but I just have want the Data field. This won't work:
mapper.readvalue(jsonString,Data.class);
How do I make Jackson read just the "Data" field?
Jackson 2.3 now has a JsonPointer class you can use. There's a simple example in their quick overview for the release.
Usage is simple: for JSON like
{
"address" : { "street" : "2940 5th Ave", "zip" : 980021 },
"dimensions" : [ 10.0, 20.0, 15.0 ]
}
you could use expressions like:
JsonNode root = mapper.readTree(src);
int zip =root.at("/address/zip").asIntValue();
double height = root.add("/dimensions/1").asDoubleValue();// assuming it's the second number in there
I think that the easiest way to do this is using the Jackson TreeModel: let Jackson parse the JSON input into a JsonNode object that you then query, assuming some knowledge of the data structure. This way you can ignore most of the data, walking down the JsonNodes to the data that you want.
// String input = The JSON data from your question
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(input.getBytes(), JsonNode.class);
// can also use ArrayNode here, but JsonNode allows us to get(index) line an array:
JsonNode hits = rootNode.get("hits");
// can also use ObjectNodes here:
JsonNode oneHit = null;
JsonNode dataObj = null;
int idx = 0;
Data data = null;
if (hits != null)
{
hits = hits.get("hit");
if (hits != null)
{
while ((oneHit = hits.get(idx)) != null)
{
dataObj = oneHit.get("data");
System.out.println("Data[" + idx + "]: " + dataObj);
idx++;
}
}
}
Output:
Data[0]: {"id":"tt1185834","data":{"actor":["Abercrombie, Ian","Baker, Dee","Burton, Corey"],"title":["Star Wars: The Clone Wars"]}}
Data[1]: {"id":"tt0121766","data":{"actor":["Bai, Ling","Bryant, Gene","Castle-Hughes, Keisha"],"title":["Star Wars: Episode III - Revenge of the Sith"]}}
You can still use your Data class implementation, but I believe this will require getting the String representing each data - as above relying on toString, or using JsonNode.getText() - and re-parsing it using the ObjectMapper:
mapper.readValue(dataArray, Data.class));
The alternative is to use the Jackson Streaming Model, and intercept the nodes yourself until you see the part of the input that marks the beginning of each data element, then consume the string and call objectMapper.readValue on the contents, for each string.
Json-path could be a very good alternative for such a requirement - if you are okay with a solution other than Jackson that is: http://code.google.com/p/json-path/

How to properly format JSON string in java?

I have a jersey client that is getting JSON from a source that I need to get into properly formatted JSON:
My JSON String looks like the folllowing when grabbing it via http request:
{
"properties": [
{
someproperty: "aproperty",
set of data: {
keyA: "SomeValueA",
keyB: "SomeValueB",
keyC: "SomeValueC"
}
}
]
}
I am having problems because the json has to be properly formatted and keyA, keB, and keyC are not surrounded in quotes. Is there some library that helps add quotes or some best way to go about turning this string to properly formatted json? Or if there is some easy way to convert this to a json object without writing a bunch of classes with variables and lists that match the incoming structure?
you can use json-lib. it's very convenient! you can construct your json string like this:
JSONObject dataSet = new JSONObject();
dataSet.put("keyA", "SomeValueA") ;
dataSet.put("keyB", "SomeValueB") ;
dataSet.put("keyC", "SomeValueC") ;
JSONObject someProperty = new JSONObject();
dataSet.put("someproperty", "aproperty") ;
JSONArray properties = new JSONArray();
properties.add(dataSet);
properties.add(someProperty);
and of course you can get your JSON String simply by calling properties.toString()
I like Flexjson, and using lots of initilizers:
public static void main(String[] args) {
Map<String, Object> object = new HashMap<String, Object>() {
{
put("properties", new Object[] { new HashMap<String, Object>() {
{
put("someproperty", "aproperty");
put("set of dada", new HashMap<String, Object>() {
{
put("keyA", "SomeValueA");
put("keyB", "SomeValueB");
put("keyC", "SomeValueC");
}
});
}
} });
}
};
JSONSerializer json = new JSONSerializer();
json.prettyPrint(true);
System.out.println(json.deepSerialize(object));
}
results in:
{
"properties": [
{
"someproperty": "aproperty",
"set of dada": {
"keyA": "SomeValueA",
"keyB": "SomeValueB",
"keyC": "SomeValueC"
}
}
]
}
Your string isn't JSON. It's something that bears a resemblance to JSON. There is no form of JSON that makes those quotes optional. AFAIK, there is no library that will reads your string and cope with the missing quotes and then spit it back out correctly. You need to find the code that produced this and repair it to produce actual JSON.
You can use argo, a simple JSON parser and generator in Java

Categories