how whould I parse JSON with numerical object keys in Jackson JSON - java

I just started using Jackson JSON parser, and I love it, but I've run into a problem with a JSON object I'm trying to parse.
here's my current java code:
public class resetPassword {
private String id;
private String key1;
private String key2;
public String getId() {
return id;
}
public void setId(String id) {
this.id= id;
}
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1= key1;
}
public String getKey2() {
return key2;
}
public void setKey2(String key2) {
this.key2= key2;
}
}
how would I parse something like this in Jackson:
{
"1":{
"key1":"val",
"key2":"val"
},
"2":{
"key":"val",
"key":"val"
}, .. etc
}
any help with this would be greatly apreceated

Based on the information in comments, I guess you need to combine traversing with data binding.
First, using traversal, get JsonNode objects with {"key1": ..., "key2": ...}.
Pseudocode (not tested):
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(genreJson);
Iterator<String> fieldNames = root.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode node = root.get(fieldName);
// now you should have {"key1": ...} in node
}
Then use data binding for each node you found:
ResetPassword item = mapper.readValue(node, ResetPassword.class);

If you need a quick way, you can set it to a Map;
ObjectMapper mapper = new ObjectMapper();
Map<String, Map<String, String>> map = mapper.readValue(br, Map.class);
System.out.println(map);
Your map would now be:
{1={key1=val, key2=val}, 2={key1=val, key2=val}}
You can iterate over the Map(s) and set your ResetPassword accordingly.
PS: br is my BufferedReader instance which reads the json placed in numeric.txt,
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("numeric.txt"), "UTF-8"));

Related

Binding a JSON to a bean with a key which can be map or an array at times

I have a dynamic incoming JSON like shown below :
1st possibility
{
"key1":"value1",
"key2":"value2",
"key3":{
"inKey1":"inValue1",
"inKey2":"inValue2"
}
}
2nd possibility
{ "key1":"value1", "key2":"value2", "key3":[{
"inKey1":"inValue1",
"inKey2":"inValue2"
},{
"inKey1":"inValue3",
"inKey2":"inValue4"
}] }
The value of key3 is generally a map. But sometimes it can come as an array as well. I have to bind this JSON to a Bean and then proceed further. I am planning to write two beans, one with key3 as a map and the other with key3 as an array. I will check if value of key3 is an instance of map or an array and then bind to the corresponding bean. Is there any optimal way to get this task done with a single bean ? Please guide me.
Beans (which I havent written yet) would be something like :
public class Bean1{
private String key1;
private String key2;
private Map<String, String> key3 = new HashMap<String, String>();
}
public class Bean2{
private String key1;
private String key2;
private Map<String, String> key3[];
}
Deserialization of different json inputs with common properties can be done through generalization (Inheritance).
Define a parent bean : Define a parent bean with common properties
public class ParentBean {
protected String key1;
protected String key2;
public ParentBean(String key1, String key2) {
super();
this.key1 = key1;
this.key2 = key2;
}
// Setters and Getters
}
Define child beans : Define child beans with special properties
Bean1
public class Bean1 extends ParentBean {
private Map<String, String> key3;
public Bean1(String key1, String key2, Map<String, String> key3) {
super(key1, key2);
this.key3 = key3;
}
// Setters and Getters
}
Bean2
public class Bean2 extends ParentBean {
private Map<String, String> key3[];
public Bean2(String key1, String key2, Map<String, String>[] key3) {
super(key1, key2);
this.key3 = key3;
}
// Setters and Getters
}
Design a Deserializer : Since the framework is not specified, I have taken the liberty of using jackson framwork. Using jackson a Deserializer can be designed as follows:
public class ParentBeanDeserializer extends StdDeserializer<ParentBean>{
public ParentBeanDeserializer() {
this(null);
}
public ParentBeanDeserializer(Class<?> c) {
super(c);
}
#Override
public ParentBean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException{
ParentBean pb;
JsonNode node = jp.getCodec().readTree(jp);
String value1 = node.findValue("key1").asText();
String value2 = node.findValue("key2").asText();
JsonNode node3 = node.findValue("key3");
ObjectMapper mapper = new ObjectMapper();
if(node3.isArray()){
String json3 = node3.toString();
Map<String, String>[] map = mapper.readValue(json3, Map[].class);
pb = new Bean2(value1,value2,map);
}
else{
String json3 = node3.toString();
Map<String, String> map = mapper.readValue(json3, Map.class);
pb = new Bean1(value1,value2,map);
}
return pb;
}
}
Usage : Beans/Classes mentioned above can be used as follows:
String json = getJasonResponseAsText();
ObjectMapper om = new ObjectMapper();
SimpleModule mod = new SimpleModule();
mod.addDeserializer(ParentBean.class, new ParentBeanDeserializer());
om.registerModule(mod);
ParentBean pb = om.readValue(json, ParentBean.class);
if (pb instanceof Bean1) {
Bean1 b1 = (Bean1)pb;
//Perform Bean1 related activites
}
else if (pb instanceof Bean2) {
Bean2 b2 = (Bean2)pb;
//Perform Bean2 related activites
}
I'm using org.json the main thing is to check whether the key is instanceof object or array. when you get to know the kind of the key. you can put your logic easily.
In the first case you have object then i simply typecast into object and fetch the values.
in the second case you have the array so i typecast into JSONArary and used a simple for loop to gather the elements.
Code:
package jsontest;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
/**
*
* #author Sahil
*/
class Model1{
String key1;
String key2;
List<Model2> key3;
Model1(){
key3 = new ArrayList<>();
}
#Override
public String toString() {
String s = "key1=" + key1 + ", key2=" + key2;
for(Model2 m: key3){
s+="\n"+"inKey1="+m.key1+", inkey2="+m.key2;
}
return s;
}
}
class Model2{
String key1;
String key2;
}
public class JSONTest {
static Model1 parse(String s){
JSONObject object = new JSONObject(s);
Model1 model = new Model1();
model.key1 = object.getString("key1");
model.key2 = object.getString("key2");
if (object.get("key3") instanceof JSONObject){
JSONObject key3 = object.getJSONObject("key3");
Model2 model2 = new Model2();
model2.key1 = key3.getString("inKey1");
model2.key2 = key3.getString("inKey2");
model.key3.add(model2);
}else{
JSONArray array = object.getJSONArray("key3");
for(int i=0;i<array.length();i++){
JSONObject row = array.getJSONObject(i);
Model2 model2 = new Model2();
model2.key1 = row.getString("inKey1");
model2.key2 = row.getString("inKey2");
model.key3.add(model2);
}
}
return model;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String firstCase = "{\r\n\"key1\":\"value1\",\r\n\"key2\":\"value2\",\r\n\"key3\":{\r\n \"inKey1\":\"inValue1\",\r\n \"inKey2\":\"inValue2\"\r\n }\r\n}";
String secondCase = "{ \"key1\":\"value1\", \"key2\":\"value2\", \"key3\":[{\r\n \"inKey1\":\"inValue1\",\r\n \"inKey2\":\"inValue2\"\r\n },{\r\n \"inKey1\":\"inValue3\",\r\n \"inKey2\":\"inValue4\"\r\n }] }";
System.out.println(parse(firstCase));
System.out.println("---------");
System.out.println(parse(secondCase));
}
}
Result:
key1=value1, key2=value2
inKey1=inValue1, inkey2=inValue2
---------
key1=value1, key2=value2
inKey1=inValue1, inkey2=inValue2
inKey1=inValue3, inkey2=inValue4

How to combine these 2 jackson serializations to a single one?

Here's my Pojo:
public static class MyPojo {
private int rootId;
private String command;
private Double value;
// I want this property to be shown at root level
#JsonIgnore
public int getRootId() {
return rootId;
}
public void setRootId(int rootId) {
this.rootId = rootId;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
}
Here's my first serialization method:
public static void writeJsonId6() throws JsonProcessingException {
MyPojo pojo = new MyPojo();
pojo.setRootId(6);
pojo.setCommand("property.batch");
pojo.setValue(129.00);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
String json = mapper.writer().withRootName(Integer.toString(pojo.getRootId())).writeValueAsString(pojo);
System.out.println(json);
}
Json Output: {"6":{"command":"property.batch","value":129.0}}
Here's my second serialization method:
public static void writeJsonId7() throws JsonProcessingException {
MyPojo pojo = new MyPojo();
pojo.setRootId(7);
pojo.setCommand("property.batch");
pojo.setValue(88.00);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
String json = mapper.writer().withRootName(Integer.toString(pojo.getRootId())).writeValueAsString(pojo);
System.out.println(json);
}
Json Output: {"7":{"command":"property.batch","value":88.0}}
This is what I need:
{"6":{"command":"property.batch","value":129.0}, "7":{"command":"property.batch","value":88.0}}
Jackson lib versions: jackson-core:2.0.0, jackson-databind:2.9.0
Just create a Map<String, MyPojo>, containing "6" and "7" as keys, and the respective POJOs as values, and serialize that map.
You can try using Json Streams:
JsonFactory factory = new JsonFactory();
JsonGenerator generator = factory.createGenerator(new PrintWriter(System.out));
generator.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
generator.writeStartObject();
generator.writeObject(firstPojo);
generator.writeObject(secondPojo);
generator.writeEndObject();

How to create a JSON array with this specific structure?

I intend to create a JSON Array with the following structure. The metadata tag is going to constant in all the entries. I am stumped.
[{
"metadata": {
"Value": "String"
},
"name": "String",
"id": "String"
},
{
"metadata": {
"Value": "String"
},
"name": "String",
"id": "String"
}
]
public class yourJsonObject {
private Map<String, String> metadata;
private String name;
private string id;
public yourJsonObject() {
}
public Map<String, String> getMetadata(){
return metadata;
}
public void setMetadata(Map<String, String> metadata){
this.metadata = metadata;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId(){
return id;
}
public void setId(String id){
this.id = id;
}
}
Then somewhere else you can just do this:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
yourJsonObject example = new yourJsonObject(); // have your POJO you want to save
mapper.writeValue(new File("result.json"), example);
To read you can just use:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
yourJsonObject value = mapper.readValue(new File("data.json"), yourJsonObject .class);
Both snippets are taken from my linked wiki article from jackson themselves.
Jackson should automatically be able to parse this POJO to an equivalent JSON if configured correctly.
Note: Jackson has to be globally registered and has to know about it. Please read the wiki of what you use to know about it... Jackson in 5 Minutes
Else you could just manually build the JSON like Neeraj said.
JSONArray array = new JSONArray(); // Create JSONArray Object
JSONObject jsonObject = new JSONObject(); // Your JSONObject which gets added into array
jsonObject.put("metadata",new MetaDataCustomClass("SomeRandomStringValue"));
jsonObject.put("name", "Neeraj");
jsonObject.put("id", "123");
array.add(jsonObject); // Here you push the jsonObject into Array.
Note: MetaDataCustomClass is just a custom Class having a Value instance variable of type String.
Class MetaDataCustomClass {
private String value;
public MetaDataCustomClass(String value){
this.value = value;
}
}

Deserialize nested array as ArrayList with Jackson

I have a piece of JSON, that looks like this:
{
"authors": {
"author": [
{
"given-name": "Adrienne H.",
"surname": "Kovacs"
},
{
"given-name": "Philip",
"surname": "Moons"
}
]
}
}
I have created a class to store Author information:
public class Author {
#JsonProperty("given-name")
public String givenName;
public String surname;
}
And two wrapper classes:
public class Authors {
public List<Author> author;
}
public class Response {
public Authors authors;
}
This is working, but having two wrapper classes seems to be unnecessary. I want to find a way to remove Authors class and have a list as a property of Entry class. Is something like that is possible with Jackson?
Update
Solved that with custom deserializer:
public class AuthorArrayDeserializer extends JsonDeserializer<List<Author>> {
private static final String AUTHOR = "author";
private static final ObjectMapper mapper = new ObjectMapper();
private static final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
#Override
public List<Author> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
ObjectNode objectNode = mapper.readTree(jsonParser);
JsonNode nodeAuthors = objectNode.get(AUTHOR);
if (null == nodeAuthors // if no author node could be found
|| !nodeAuthors.isArray() // or author node is not an array
|| !nodeAuthors.elements().hasNext()) // or author node doesn't contain any authors
return null;
return mapper.reader(collectionType).readValue(nodeAuthors);
}
}
And using it like this:
#JsonDeserialize(using = AuthorArrayDeserializer.class)
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
Thanks #wassgren for the idea.
I see at least two approaches to do this if you want to get rid of wrapper classes. The first is to use the Jackson Tree Model (JsonNode) and the second is to use a deserialization feature called UNWRAP_ROOT_VALUE.
Alternative 1: Use JsonNode
When deserializing JSON using Jackson there are multiple ways to control what type of objects that are to be created. The ObjectMapper can deserialize the JSON to e.g. a Map, JsonNode (via the readTree-method) or a POJO.
If you combine the readTree-method with the POJO conversion the wrappers can be completely removed. Example:
// The author class (a bit cleaned up)
public class Author {
private final String givenName;
private final String surname;
#JsonCreator
public Author(
#JsonProperty("given-name") final String givenName,
#JsonProperty("surname") final String surname) {
this.givenName = givenName;
this.surname = surname;
}
public String getGivenName() {
return givenName;
}
public String getSurname() {
return surname;
}
}
The deserialization can then look something like this:
// The JSON
final String json = "{\"authors\":{\"author\":[{\"given-name\":\"AdrienneH.\",\"surname\":\"Kovacs\"},{\"given-name\":\"Philip\",\"surname\":\"Moons\"}]}}";
ObjectMapper mapper = new ObjectMapper();
// Read the response as a tree model
final JsonNode response = mapper.readTree(json).path("authors").path("author");
// Create the collection type (since it is a collection of Authors)
final CollectionType collectionType =
TypeFactory
.defaultInstance()
.constructCollectionType(List.class, Author.class);
// Convert the tree model to the collection (of Author-objects)
List<Author> authors = mapper.reader(collectionType).readValue(response);
// Now the authors-list is ready to use...
If you use this Tree Model-approach the wrapper classes can be completely removed.
Alternative 2: remove one of the wrappers and unwrap the root value
The second approach is to remove only one of the wrappers. Assume that you remove the Authors class but keep the Response-wrapper. If you add the a #JsonRootName-annotation you can later unwrap the top-level name.
#JsonRootName("authors") // This is new compared to your example
public class Response {
private final List<Author> authors;
#JsonCreator
public Response(#JsonProperty("author") final List<Author> authors) {
this.authors = authors;
}
#JsonProperty("author")
public List<Author> getAuthors() {
return authors;
}
}
Then, for your mapper simply use:
ObjectMapper mapper = new ObjectMapper();
// Unwrap the root value i.e. the "authors"
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
final Response responsePojo = mapper.readValue(json, Response.class);
The second approach only removes one of the wrapper classes but instead the parsing function is quite pretty.

Serialize Map<String,Object> to Json and back

I want to serialize a Map<String,Object> to JSON with Jackson and back. The object type can be of several beans. But when serializing back I think Jackson don't knows what type object has. Thus is serializes it to a LinkedHashMap. Is it possible to add the information which type the object has?
Here is my sample program:
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
ObjectMapper m = new ObjectMapper();
JsonFactory jf = new JsonFactory();
Map<String, Object> map = new HashMap<String, Object>();
UserBean bean1 = new UserBean();
bean1.setId("1");
bean1.setName("test");
map.put("user", bean1);
String test = m.writeValueAsString(map);
map = m.readValue(test, typeRef);
for (final Map.Entry<String, Object> entry : map.entrySet())
{
final String key = entry.getKey();
if (key.matches("user"))
{
final UserBean userBean = (UserBean)map.get(key);
}
}
public class UserBean {
String id;
String name;
public UserBean()
{
super();
}
public void setId(String id){
this.id = id;
}
public String getId(){
return this.id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
Try doing the map like below;
Map<String, UserBean> map = new HashMap<String, UserBean>();
(If Java 7 you can use use the diamond operator without specifying the types like new HashMap<>()).
You could also try using #JsonDeserialize using jackson-annotations

Categories