I am trying to convert JSON into POJO.
I have worked with Jackson to convert standard JSON file.
In this particular case, I would like to overwrite the key value to "default" class/variable. In this case, there are multiple key value to be replaced (ie. hundreds, and the key values to be replaced are unknown).
Is this possible? I thought of storing it into Map, then iterate and store each into POJO, but wondering if there is different option, since I am not that familiar with storing JSON to Map.
Example of the JSON to be processed:
"People" : {
"person1" : {
"name" : "john doe",
"address" : "123 main st",
"email" : "john#doe.com"
},
"person2" : {
"name" : "bob cat",
"address" : "234 dog st",
"email" : "bob#cat.com"
},
"person3" : {
"name" : "foo bar",
"address" : "111 1st ave",
"email" : "foo#bar.com"
},
"person8" : {
"name" : "james bono",
"address" : "999 alaska st",
"email" : "james#bono.com"
}
}
Is it possible to generate the class in the following structure? The main issue is there are hundreds of value to be replaced and assuming they are unknown, I can't use this approach.
#JsonIgnoreProperties(ignoreUnknown = true)
public class People {
#JsonAlias({"person1", "person2"})
private List<Details> person; // --> this should be the default replacing person1, person2, and so on
private class Details {
String name;
String address;
String email;
}
}
You can use JsonAnySetter annotation for all properties personXYZ. See below example:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
System.out.println(mapper.readValue(jsonFile, People.class).getPersons());
}
}
class People {
private List<Details> persons = new ArrayList<>();
#JsonAnySetter
public void setPerson(String name, Details person) {
this.persons.add(person);
}
public List<Details> getPersons() {
return persons;
}
public static class Details {
String name;
String address;
String email;
// getters, setters, toString
}
}
For your JSON above code prints:
[Details{name='john doe', address='123 main st', email='john#doe.com'}, Details{name='bob cat', address='234 dog st', email='bob#cat.com'}, Details{name='foo bar', address='111 1st ave', email='foo#bar.com'}, Details{name='james bono', address='999 alaska st', email='james#bono.com'}]
In case you use inner class remember to make it public static to make it visible to Jackson instantiation process.
See also:
How to use dynamic property names for a Json object
Jackson Annotation Examples
Just adding my two cents. I think this way looks nicer and is also easy to retrieve data. Jackson will do the deserialization without the need of any other tags or custom code, and it's all nice and sorted in a hashmap.
class People {
private Map<String, Person> persons = new HashMap<>();
// getter and setters
}
public class Person {
String name;
String address;
String email;
// getters and setters
}
}
Related
I am developing an application within that I have to create the SHA256 hash for the incoming data. To make that I have to follow the specific sorting order for each property. Hence, I have created a class TemplateNodeMap which extends LinkedHashMap, within that I have specified the order that I need to follow.
Now, I would like to read each property in the incoming JSON data, add a specific field, and create the Hash string. I am a bit confused about adding the data and creating the string. I am worried if I am following the optimal procedure or not as I need to follow the process for a large amount of data.
Can someone please let me know if this is the right approach?
Following is the incoming JSON (Since JSON can have any order I need to obtain properties according to my required Hash String order):
{
"age": 30,
"name": "Batman",
"address": {
"city": "Gotham",
"street": {
"name": "Gotham 123"
}
}
}
Following is my TemplateNodeMap class:
package io.hash;
import java.util.LinkedHashMap;
public class TemplateNodeMap extends LinkedHashMap {
public TemplateNodeMap() {
put("name", null);
put("age", null);
put("address", new LinkedHashMap<>() {{
put("street", new LinkedHashMap<>() {{
put("name", null);
}});
put("city", null);
}});
}
}
Following is my ApplicationMain class which is reading and loading the data to TemplateNodeMap:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.InputStream;
public class ApplicationMain {
public static void main(String[] args) throws IOException {
final ObjectMapper objectMapper = new ObjectMapper();
final InputStream jsonStream = ApplicationMain.class.getResourceAsStream("/InputJSON.json");
final ObjectNode inputTemplate = objectMapper.readValue(jsonStream, ObjectNode.class);
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inputTemplate));
final TemplateNodeMap templateNodeMap = new TemplateNodeMap();
templateNodeMap.put("name", inputTemplate.get("name"));
templateNodeMap.put("age", inputTemplate.get("age"));
//Unable to understand how to insert the complex object values into LinkedHashMap and follow the order
}
}
I am not understanding how to add the complex object to LinkedHashMap and create a string out of it.
Not all fields are mandatory so I would like to omit the null values during the creation of Hash String.
Can someone please suggest to me how to achieve this and if this is the right approach to creating a Hash String based on the required order?
There are two jackson annotations that can help you to serialize the jackson properties in a custom order excluding the non null values:
JsonPropertyOrder annotation that can be used to define ordering (possibly partial) to use when serializing object properties.
JsonInclude annotation used to indicate when value of the annotated property, or all properties of the annotated class, is to be serialized.
You can then deserialize your json to the pojo java classes and then serialize them obtaining a new json with the expected order of the properties and without null properties:
#Data
#JsonPropertyOrder(value = {"name", "age", "address"})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
private int age;
private String name;
private Address address;
}
#Data
#JsonPropertyOrder(value = {"street", "city"})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Address {
private String city;
private Street street;
}
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Street {
private String name;
}
//you can delete for example the name property from your json
Person person = mapper.readValue(json, Person.class);
String output = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
//ok, the name property will not appear in the output because it's null
System.out.println(output);
Requirements:
"I am developing an application within that I have to create the SHA256 hash for the incoming data."
"To make that I have to follow the specific sorting order for each property."
Proposal:
Create data classes for your incoming data, order their attributes as you like
Transform data class into a 'standard' JSON representation using prettyprint
Calculate hash over 'standard' JSON representation
For completeness a manual parsing of the linked hashmap is included
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.*;
import java.nio.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
class Person {
int age; String name; Address address; // Attribute order effects output order!
public Person(){}
public Person(int age, String name, Address address) {
this.age = age; this.address = address; this.name = name;
}
public void setAge(int age){this.age = age;}
public int getAge(){return age;}
public void setName(String name){this.name = name;}
public String getName(){return name;}
public void setAddress(Address address){this.address = address;}
public Address getAddress(){return address;}
}
class Address {
String city;
Street street;
public Address(){}
public Address(String city, Street street){this.city = city; this.street = street;}
public void setCity(String city){this.city = city;}
public String getCity(){return city;}
public void setStreet(Street street){this.street = street;}
public Street getStreet(){return street;}
}
class Street {
String name;
public Street(){}
public Street(String name) {this.name = name;}
public String getName(){return name;}
public void setName(String name) {this.name = name;}
}
public class ApplicationMain {
static String inputJson = "{\"age\": 30,\"name\": \"Batman\",\"address\": {\"city\": \"Gotham\",\"street\": {\"name\": \"Gotham 123\"}}}";
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
final ObjectMapper objectMapper = new ObjectMapper();
try {
// use Paths.get("InputJSON.json").toFile() as alternative to the string
Map<?, ?> map = objectMapper.readValue(inputJson, Map.class);
System.out.println("The linked hashmap to process for sorting:");
System.out.println(map);
System.out.println("Individual elements:");
System.out.println("name: " + map.get("name"));
System.out.println("age: " + map.get("age"));
System.out.println("address:");
Map<?, ?> addressMap = (Map<?, ?>)map.get("address");
System.out.println(" city: " + addressMap.get("city"));
System.out.println(" street:");
System.out.println(" name: " + ((Map<?, ?>)addressMap.get("street")).get("name"));
} catch (Exception ex) {
ex.printStackTrace();
}
Person person = objectMapper.readValue(inputJson, Person.class);
System.out.println("Original JSON:\n" + inputJson);
String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
System.out.println("Standardized JSON:\n" + prettyJson);
byte[] hash = MessageDigest.getInstance("SHA-256").digest(prettyJson.getBytes(StandardCharsets.UTF_8));
for ( byte b : hash) {
System.out.printf("%02x", b);
}
System.out.println();
}
}
$ java -cp .:jackson-databind-2.13.3.jar:jackson-core-2.13.3.jar:jackson-annotations-2.13.3.jar ApplicationMain
The linked hashmap to process for sorting:
{age=30, name=Batman, address={city=Gotham, street={name=Gotham 123}}}
Individual elements:
name: Batman
age: 30
address:
city: Gotham
street:
name: Gotham 123
Original JSON:
{"age": 30,"name": "Batman","address": {"city": "Gotham","street": {"name": "Gotham 123"}}}
Standardized JSON:
{
"age" : 30,
"name" : "Batman",
"address" : {
"city" : "Gotham",
"street" : {
"name" : "Gotham 123"
}
}
}
5f962abf0cdc5150eb2614c629592d889d59b82499b13dd1b8d12e421bb0440a
$
You can check the hash value at https://dencode.com/hash
(The link already contains the JSON data. If you paste text there, verify that the correct line ending type is selected.)
My Spring Boot app makes a call to a REST API and receives a JSON with a varying number of entities. E.g.
{
"content": {
"guest_1": {
"name": {
"firstName": "a",
"lastName": "b"
},
"vip": false
},
"guest_2": {
"name": {
"firstName": "c",
"lastName": "d"
},
"vip": false
},
...more guests omitted...
}
}
There can be 1 to many guests and I don't know their number upfront. As you can see, they aren't in an array, they are objects instead.
I'd like to avoid deserializing into a class like
public class Content {
#JsonProperty("guest_1")
private Guest guest1;
#JsonProperty("guest_2")
private Guest guest2;
// More Guests here each having their own field
}
What I'd like to use is
public class Content {
private List<Guest> guests;
}
The #JsonAnySetter annotation I read about at https://www.baeldung.com/jackson-annotations looks promising but I couldn't get it to work.
3.2. Convert to an object at https://www.baeldung.com/jackson-json-node-tree-model looks also good but it didn't work out either.
I'm not sure if I can make Jackson do this in a declarative way or I should write a custom JsonDeserializer. Could you please help me?
#JsonAnySetter will work as it allows to specify a POJO type as second parameter. You could recreate the example JSON as, omitting setXXX() and getXXX() methods on POJOs for clarity:
private static class Content {
private Guests content;
}
private static class Guests {
private List<Guest> guests = new ArrayList<>();
#JsonAnySetter
private void addGuest(String name, Guest value) {
guests.add(value);
}
}
private static class Guest {
private Name name;
private boolean vip;
}
private static class Name {
private String firstName;
private String lastName;
}
With your JSON example will produce:
Content root = new ObjectMapper().readValue(json, Content.class);
root.getContent().getGuests().stream()
.map(Guest::getName)
.map(Name::getFirstName)
.forEach(System.out::println); // a, c
I'm wondering if there is any way to deserialize several JSON fields to just one Java property. E.g. given this JSON:
{
"id" : "1",
"name" : "Bartolo",
"address" : "whatever",
"phone" : "787312212"
}
deserialize it to this class:
public class Person {
public String id;
public String name:
#JsonProperty(names = {"address", "phone"}) //something like this
public String moreInfo;
}
so moreInfo equals to "whatever, 787312212" or something similar.
Is this possible without using custom deserializer?
You could use the #JsonCreator annotation like following:
String json = {"id" : "1", "name" : "Bartolo", "address" : "whatever", "phone" : "787312212" }
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json , Person.class);
and in the constructor of your Person class add this
#JsonCreator
public Person(#JsonProperty("address") String address, #JsonProperty("phone") String phone) {
this.moreInfo = address + "," phone;
}
Another solution, if you don't want to know/handle other fields in the object, but decided to still receive these fields (maybe for logging purposes), then you can put them in a key-value store(Map)
#Getter
private final Map<String, Object> otherFields = new HashMap<>();
#JsonAnySetter
public void set(String name, Object value) {
otherFields.put(name, value);
}
Note that if you have any field with the same name as the Map field(like 'otherFields' in the example above), then you can get MismatchedInputException
Lets say we have the following JSON example:
{
"teachers": [{
"id": "abc",
"payment": 10,
"name": "xyz",
"clases": ["1", "3"]
}, {
"id": "qwe",
"payment": 12,
"name": "xcv",
"classes": ["1", "2"]
}],
"classes": [{
"id": "1",
"room": 7
}, {
"id": "2",
"room": 1
}, {
"id": "3",
"room": 2
}]
}
I would like to deserialize it to Java objects (getters/setters ommited):
class Teacher {
private String id;
private double payment;
private String name;
private List<CLassRoom> classRooms;
}
class ClassRoom {
private String id;
private int room;
}
As you see, we have a references here. I know I can deserialize it with Jackson (and would like to) but the problem is that I cannot touch DTO itself (so annotations are not possible, would also like to avoid wrappers (many classes)). Also, it would be nice if the "configuration" of deserialization was in separate file (json schema for example). I would also like to avoid some tags given by user - he should only pass me the values. Moreover, he should know where is the error, if he made some mistake.
Also, it would be nice if I could manipulate name of field in json (some clients may have different habits).
I didn't find anything which satisffied all of above requirements(entity reference and error handling are the most important). However - I just have heard about json schema, so maybe it provides such functionality (but I didn't find it though). Any helpful reference/example/lib? I will appreciate any help.
Just to be correct - imagine that the given json is a RELATIONAL database snapshot of the instance. I just want to create whole entity like the hibernate (or actually JPA) does :)
1. add jar of import org.json.JSONObject.
2. JSONObject object = new JSONObject(list)
2.1 object.has("teachers") if it is exists
2.2 JSONArray teacherArray = (JSONArray) object.get("teachers");
2.3 JSONObject teacherJsonObject = teacherArray .getJSONObject(0);
(if you have more than jsonobject in json arrary then itrate it.)
2.4 if(teacherJsonObject .has("id"))//you can check existence like this.
String id=teacherJsonObject .getString("id");
String payment=teacherJsonObject .getString("payment");
String name=teacherJsonObject .getString("name");
It may not be the best solution, but it's a working one.
Let's create a Parser class like the following:
public class Parser {
private List<Teacher> teachers;
private List<ClassRoom> classes;
public void parse() {
for (Teacher teacher : teachers) {
for (String classRoomId : teacher.getClasses()) {
for (ClassRoom classRoom : classes) {
if (classRoom.getId().equals(classRoomId)) {
teacher.getClassRooms().add(classRoom);
}
}
}
}
}
}
Modify your ClassRoom class to have a getter on the id field:
public class ClassRoom {
private String id;
private int room;
public String getId() {
return id;
}
}
And your Teacher class to get the Ids of classes AND the classRooms references:
public class Teacher {
private String id;
private double payment;
private String name;
private String[] classes;
private List<ClassRoom> classRooms = new ArrayList<>();
public String[] getClasses() {
return classes;
}
public List<ClassRoom> getClassRooms() {
return classRooms;
}
}
If you use the Gson library, you could then just parse your JSON like that:
Gson gson = new Gson();
Parser parser = gson.fromJson(jsonString, Parser.class);
parser.parse;
Now, every teacher will have their classRooms correctly referenced.
I am very new to Json and my goal to create the Json output below from Java bean. How should I structure my Java object? Should I have MyResult class and User and Result as subclasses? What Json library can I use for this?
“MyResult” {
“AccountID”: “12345”,
"User" {
"Name": "blah blah",
"Email": “blah#blah.com”,
},
"Result" {
"Course": “blah”,
"Score": “10.0”
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
How should I structure my Java object?
Below is what your object model could look like. MOXy's JSON binding leverages JAXB annotations for mapping the domain model to JSON, so I have included those as well. JAXB implementations have default rules for mapping field/property names, but since your document differs from the default each field had to be annotated.
MyResult
package forum11001458;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="MyResult")
public class MyResult {
#XmlElement(name="AccountID")
private String accountID;
#XmlElement(name="User")
private User user;
#XmlElement(name="Result")
private Result result;
}
User
package forum11001458;
import javax.xml.bind.annotation.XmlElement;
public class User {
#XmlElement(name="Name")
private String name;
#XmlElement(name="Email")
private String email;
}
Result
package forum11001458;
import javax.xml.bind.annotation.XmlElement;
public class Result {
#XmlElement(name="Course")
private String course;
#XmlElement(name="Score")
private String score;
}
What Json library can I use for this?
Below is how you can use MOXy to do the JSON binding:
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties with the following entry in the same package as your domain model:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
Note how MOXy's JSON binding does not require any compile time dependencies. All the necessary APIs are available in Java SE 6. You can add the necessary supporting APIs if you are using Java SE 5.
package forum11001458;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(MyResult.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
File json = new File("src/forum11001458/input.json");
Object myResult = unmarshaller.unmarshal(json);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(myResult, System.out);
}
}
input.json/Output
{
"MyResult" : {
"AccountID" : "12345",
"User" : {
"Name" : "blah blah",
"Email" : "blah#blah.com"
},
"Result" : {
"Course" : "blah",
"Score" : "10.0"
}
}
}
Googles GSON is a really nice json lib. This is from the previous link and it basically outlines some of its functionality.
jackson is also pretty fast and easy to use
Although closed, this SO post can help you understand the differences between Jackson and GSON. Which one is "best" depends on what is important for you.
EDIT: Specifically for Jackson, your example looks a lot like the example they give for what they call Full Data Binding, you can read it here. Btw, although the announced 5 minutes needed to read that document is maybe a bit short, it gives a complete overview of the different ways Jackson can be used. You'll also notice that the examples given do not use annotations.
Or GSON
Super easy (no getters/settres, no annotations or configurations needed).
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
}
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);
==> json is {"value1":1,"value2":"abc"}
What Json library can I use for this? Jackson Library is used to serialize Java objects into JSON and deserialize JSON string into Java objects. Add the following dependencies to pom.xml.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.4</version>
</dependency>
This dependency will transitively add the following libraries to the classpath:
jackson-annotations-2.9.4.jar
jackson-core-2.9.4.jar
jackson-databind-2.9.4.jar
**Note: Please always go with the latest jars.
How should I structure my Java object? Please see the full working code.
**MainClass.java:**
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class MainClass {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Result result = new Result();
result.setCourse("blah");
result.setScore("10.0");
User user = new User();
user.setName("blah blah");
user.setEmail("blah#blah.com");
MyResult myResult = new MyResult();
myResult.setAccountID("12345");
myResult.setResult(result);
myResult.setUser(user);
MyPojo myPojo = new MyPojo();
myPojo.setMyResult(myResult);
String jsonStr = mapper.writeValueAsString(myPojo);
System.out.println(jsonStr);
} }
**MyPojo.java:-**
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({ "AccountID", "User", "Result" })
public class MyPojo {
private MyResult MyResult;
public MyResult getMyResult() {
return MyResult;
}
#JsonProperty("MyResult")
public void setMyResult(MyResult MyResult) {
this.MyResult = MyResult;
} }
**MyResult.java:**
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({ "AccountID", "User", "Result" })
public class MyResult {
private User User;
private Result Result;
private String AccountID;
public User getUser() {
return User;
}
#JsonProperty("User")
public void setUser(User User) {
this.User = User;
}
public Result getResult() {
return Result;
}
#JsonProperty("Result")
public void setResult(Result Result) {
this.Result = Result;
}
public String getAccountID() {
return AccountID;
}
#JsonProperty("AccountID")
public void setAccountID(String AccountID) {
this.AccountID = AccountID;
} }
**Result.java:**
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({ "Course", "Score" })
public class Result {
private String Course;
private String Score;
public String getCourse() {
return Course;
}
#JsonProperty("Course")
public void setCourse(String Course) {
this.Course = Course;
}
public String getScore() {
return Score;
}
#JsonProperty("Score")
public void setScore(String Score) {
this.Score = Score;
} }
**User.java:**
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({ "Name", "Email" })
public class User {
private String Name;
private String Email;
public String getName() {
return Name;
}
#JsonProperty("Name")
public void setName(String Name) {
this.Name = Name;
}
public String getEmail() {
return Email;
}
#JsonProperty("Email")
public void setEmail(String Email) {
this.Email = Email;
}
#Override
public String toString() {
return "ClassPojo [Name = " + Name + ", Email = " + Email + "]";
} }
**Result:**
{
"MyResult" : {
"AccountID" : "12345",
"User" : {
"Name" : "blah blah",
"Email" : "blah#blah.com"
},
"Result" : {
"Course" : "blah",
"Score" : "10.0"
}
}
}
Note: Please note the use of Json Annotations like #JsonProperty("Email") to make json property names as same in the expected output & #JsonPropertyOrder({ "Name", "Email" } to maintain the sequence as in expected output. Refer: https://www.baeldung.com/jackson-annotations.