Create Jackson JSON in Java - java

I am a beginner of Jackson. How can I create a JSON message like this using Java?
{
"name": "John",
"age": "40",
"family": {
"parents_name": [
"David",
"Susan"
],
"children": "yes",
"children_names": [
"Peter",
"Mary"
]
}
}

Create a Person class in Java, with properties such as getName(), getAge() and so on. Then Jackson can create that JSON for you automatically, from your Person object.

The easiest way to do this for a beginner is to eliminate unnecessary nesting and rely on Jackson's default object binding.
You would create a class like this:
public class Person {
private String name;
private int age;
private List<String> parentNames;
private List<String> childrenNames;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getParentNames() {
return parentNames;
}
public void setParentNames(List<String> parentNames) {
this.parentNames = parentNames;
}
public List<String> getChildrenNames() {
return childrenNames;
}
public void setChildrenNames(List<String> childrenNames) {
this.childrenNames = childrenNames;
}
}
Then you can instantiate a Person from JSON like this:
Person p = ObjectMapper.readValue(jsonString, Person.class);
Note that the JSON you have in your example won't work with this object for three reasons:
The Person class has no Family object. I felt that adds unnecessary complexity. If you want that, create a separate Family class, and Person would contain a Family member (no pun intended).
I don't have a boolean for children because that can be deduced from the length of the childrenNames list.
The JSON will need to have childrenNames and parentNames rather than children_names and parents_name. If you want those, add #JsonProperty with the desired property names on the getters and setters for those values.

I gather from your comments to Vidya's solution that your looking for more flexibility than you get can get with the default binding.
Jackson allows you to create your own custom serializer. For example:
public class Person {
private String name;
private int age;
private List<String> parentsName;
private List<String> childrenNames;
public Person(String name, List<String> parentsName) {
this(name, parentsName, -1, Collections.<String>emptyList());
}
public Person(String name, List<String> parentsName, int age) {
this(name, parentsName, age, Collections.<String>emptyList());
}
public Person(String name, List<String> parentsName, int age, List<String> childrenNames) {
this.name = name;
this.age = age;
this.parentsName = parentsName;
this.childrenNames = childrenNames;
}
private void serialize(JsonGenerator generator, SerializerProvider arg2) throws IOException {
generator.writeStartObject();
generator.writeObjectField("name", name);
if (age >= 0)
generator.writeNumberField("age", age);
// start family subset
generator.writeObjectFieldStart("family");
generator.writeArrayFieldStart("parents_name");
for (String parent : parentsName) {
generator.writeObject(parent);
}
generator.writeEndArray();
generator.writeObjectField("children", (childrenNames.isEmpty() ? "no" : "yes"));
generator.writeArrayFieldStart("children_names");
for (String child : childrenNames) {
generator.writeObject(child);
}
generator.writeEndArray();
generator.writeEndObject();
// end family subset
generator.writeEndObject();
}
public static JsonSerializer<Person> createJsonSerializer() {
return new JsonSerializer<Person>() {
#Override
public void serialize(Person me, JsonGenerator generator, SerializerProvider arg2) throws IOException, JsonProcessingException {
me.serialize(generator, arg2);
}
};
}
public static void main(String[] args) throws IOException {
List<String> parentsName = Arrays.<String>asList("David", "Susan");
List<String> childrenNames = Arrays.<String>asList("Peter", "Mary");
Person person = new Person("John", parentsName, 40, childrenNames);
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("PersonModule", new Version(1, 0, 0, null));
simpleModule.addSerializer(Person.class, Person.createJsonSerializer());
// pretty output for debugging
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
mapper.registerModule(simpleModule);
System.out.println("Person json: ");
System.out.println(mapper.writeValueAsString(person));
}
}
This gives you increased flexibility in two ways:
You can apply conditional logic in serialization
You can have multiple custom serializers
The downsides are fairly obvious
More complicated
More time to implement. The default bindings were almost free. This solution is not.

Related

How to deserialize json empty array

I have a JSON looks like the following:
{
"name": "John",
"age": 20,
"skills": [
]
}
the skills if it's not empty looks like the following:
{
"skills": [
"skill_1": {
},
"skill_2": {
}]
}
and I need to deserialize this JSON to POJO:
public class Profile {
public String name;
public int age;
#JsonDeserialize(using = SkillsMapDeserializer.class)
public Map<String, Skill> skills;
}
public class Skill {
public String skillName;
public int age;
}
and my SkillsMapDeserializer looks like the following:
public class SkillsMapDeserializer extends JsonDeserializer<Map<String, Skill>> {
#Override
public Map<String, Skill> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final Map<String, Skill> map = jsonParser.readValueAs(new TypeReference<Map<String, Skill>>() {
});
if (map == null) {
return new HashMap<>();
}
return map;
}
}
if the skills aren't empty all works fine, but if the skills are an empty array I get an exception that looks like the following:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap<java.lang.Object,java.lang.Object>` out of START_ARRAY token
How can I fix this issue?
From your json data, it seem skills is an array of object.
"skills": [],
"skills": [
"skill_1": {},
"skill_2": {}
]
But your java define it as Map
public Map<String, Skill> skills;
That's why you got an exception when trying convert array to map directly.
If you can't change the POJOs Profile, you should have an mediate step to convert list to Map.
public class SkillsMapDeserializer extends JsonDeserializer<Map<String, Skill>> {
#Override
public Map<String, Skill> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final List<Map<String,Skill>> skills = jsonParser.readValueAs(new TypeReference<List<Map<String,Skill>>>>() {
});
return functionConvertListToMapWithParam(skills);
}
}
skills is not a map. it should be list of objects. try to modify your POJO like below:-
public class Profile {
#JsonProperty("name")
private String name;
#JsonProperty("age")
private Integer age;
#JsonProperty("skills")
private List < Object > skills = null;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("age")
public Integer getAge() {
return age;
}
#JsonProperty("age")
public void setAge(Integer age) {
this.age = age;
}
#JsonProperty("skills")
public List < Object > getSkills() {
return skills;
}
#JsonProperty("skills")
public void setSkills(List < Object > skills) {
this.skills = skills;
}
}

Jackson custom serializer with conditionally hidden members produces invalid JSON

I need to create a custom serializer that conditionally skips fields.
In contrast to the case described in
Skip objects conditionally when serializing with jackson my class contains a POJO member.
A PersonalInfo has a Address as member. In case the Address is hidden the resulting JSON still has the "address" tag, but without value.
I could not figure out how to fix this.
Creating a custom serializer on the ObjectMapper (see 3. at http://www.baeldung.com/jackson-custom-serialization) leads to the exact same result.
Here is the adapted code from the referenced question that shows the problem:
public class JacksonHide {
#JsonIgnoreProperties("hidden")
public static interface IHideable {
boolean isHidden();
}
public static class Address implements IHideable {
public final String city;
public final String street;
public final boolean hidden;
public Address(String city, String street, boolean hidden) {
this.city = city;
this.street = street;
this.hidden = hidden;
}
#Override
public boolean isHidden() {
return hidden;
}
}
public static class PersonalInfo implements IHideable {
public final String name;
public final int age;
public final Address address;
public final boolean hidden;
public PersonalInfo(String name, int age, Address address, boolean hidden) {
this.name = name;
this.age = age;
this.address = address;
this.hidden = hidden;
}
#Override
public boolean isHidden() {
return hidden;
}
}
private static class MyBeanSerializerModifier extends BeanSerializerModifier {
#Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if (IHideable.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new MyIHideableJsonSerializer((JsonSerializer<IHideable>) serializer);
}
return super.modifySerializer(config, beanDesc, serializer);
}
private static class MyIHideableJsonSerializer extends JsonSerializer<IHideable> {
private final JsonSerializer<IHideable> serializer;
public MyIHideableJsonSerializer(JsonSerializer<IHideable> serializer) {
this.serializer = serializer;
}
#Override
public void serialize(IHideable value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (!value.isHidden()) {
serializer.serialize(value, jgen, provider);
}
}
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new MyBeanSerializerModifier());
mapper.registerModule(module);
PersonalInfo p1 = new PersonalInfo("John", 30, new Address("A", "B", false), false);
PersonalInfo p2 = new PersonalInfo("Ivan", 20, new Address("C", "D", true), true);
PersonalInfo p3 = new PersonalInfo("Mary", 40, new Address("C", "D", true), false);
Address a1 = new Address("A", "B", false);
Address a2 = new Address("C", "D", true);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(Arrays.asList(p1, p2, p3, a1, a2)));
}
}
UPDATE:
Thanks to the feedback I have now a version based on #JSONFilter that gives me at least valid JSON. Unfortunately the nodes are still there, but are empty now ({}). How can I completely get rid of them?
public class JacksonFilterHide {
#JsonFilter("HiddenFilter")
#JsonIgnoreProperties("hidden")
public static interface IHideable {
boolean isHidden();
}
public static class Address implements IHideable {
public final String city;
public final String street;
public final boolean hidden;
public Address(String city, String street, boolean hidden) {
this.city = city;
this.street = street;
this.hidden = hidden;
}
#Override
public boolean isHidden() {
return hidden;
}
}
public static class PersonalInfo implements IHideable {
public final String name;
public final int age;
public final Address address;
public final boolean hidden;
public PersonalInfo(String name, int age, Address address, boolean hidden) {
this.name = name;
this.age = age;
this.address = address;
this.hidden = hidden;
}
#Override
public boolean isHidden() {
return hidden;
}
}
static final PropertyFilter hiddenFilter = new SimpleBeanPropertyFilter() {
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (include(writer)) {
if (pojo instanceof IHideable && ((IHideable) pojo).isHidden()) {
return;
} else {
writer.serializeAsField(pojo, jgen, provider);
return;
}
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
#Override
protected boolean include(BeanPropertyWriter writer) {
return true;
}
#Override
protected boolean include(PropertyWriter writer) {
return true;
}
};
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// ObjectMapper mapper = UserInteractionModel.getMapper();
FilterProvider filters = new SimpleFilterProvider().addFilter("HiddenFilter", hiddenFilter);
mapper.setFilters(filters);
mapper.enable(SerializationFeature.INDENT_OUTPUT);
PersonalInfo p1 = new PersonalInfo("John", 30, new Address("A", "B", false), false);
PersonalInfo p2 = new PersonalInfo("Ivan", 20, new Address("C", "D", true), true);
PersonalInfo p3 = new PersonalInfo("Mary", 40, new Address("C", "D", true), false);
Address a1 = new Address("A", "B", false);
Address a2 = new Address("C", "D", true);
System.out.println(mapper.writeValueAsString(Arrays.asList(p1, p2, p3, a1, a2)));
}
}
Output now is:
[ { "name" : "John", "age" : 30, "address" : {
"city" : "A",
"street" : "B" } }, { }, { "name" : "Mary", "age" : 40, "address" : { } }, { "city" : "A", "street" : "B" }, { } ]
Expected:
[ { "name" : "John", "age" : 30, "address" : {
"city" : "A",
"street" : "B" } }, { "name" : "Mary", "age" : 40, }, { "city" : "A", "street" : "B" } ]
Update2
Temporary fix by traversing the tree and removing empty nodes. Ugly, but works for now. Still looking for a better answer.
private void removeEmptyNodes(JSONObject json) {
Iterator<String> iter = json.keys();
while (iter.hasNext()) {
String key = iter.next();
JSONObject node;
try {
node = json.getJSONObject(key);
} catch (JSONException e) {
continue;
}
if (node.length() == 0) {
iter.remove();
} else {
removeEmptyNodes(node);
}
}
}
Solution inspired by this question: How do I remove empty json nodes in Java with Jackson?
Your serializer is broken: it CAN NOT choose to NOT WRITE a value, if requested. In case of writing a property value, caller has already written out property name, so not writing the value will indeed break output.
This either results in an exception being thrown (ideally), or broken output (less ideally); regardless, JsonSerializer is not allowed to try to decide whether value is being written.
To exclude a property being serialized, your valid choices include:
Static property annotations like #JsonIgnore and #JsonIgnoreProperties that always exclude specific named property
Static annotation #JsonInclude that bases inclusion on type of value (no nulls, no absents, no empty values)
Static definition but dynamic choice of #JsonView to use (sets of properties you can dynamically exclude by associating with a view)
Dynamic #JsonFilter
Custom serializer for type that contains property

Binding json, that has a list, with an object using Jackson

Given I have the following json:
{
"Company": {
"name": "cookieltd",
"type": "food",
"franchise_location": [
{
"location_type": "town",
"address_1": "5street"
},
{
"location_type": "village",
"address_1": "2road"
}
]
}
}
How can it be binded to the following object classes using Jackson?:
1) Company class
public class Company
{
String name, type;
List<Location> franchise_location = new ArrayList<Location>();
[getters and setters]
}
2) Location class
public class Location
{
String location_type, address_1;
[getters and setters]
}
I have done:
String content = [json above];
ObjectReader reader = mapper.reader(Company.class).withRootName("Company"); //read after the root name
Company company = reader.readValue(content);
but I am getting:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "franchise_location"
As far as I can tell, you are simply missing an appropriately named getter for the field franchise_location. It should be
public List<Location> getFranchise_location() {
return franchise_location;
}
(and the setter)
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
Alternatively, you can annotate your current getter or field with
#JsonProperty("franchise_location")
private List<Location> franchiseLocation = ...;
which helps to map JSON element names that don't really work with Java field name conventions.
The following works for me
public static void main(String[] args) throws Exception {
String json = "{ \"Company\": { \"name\": \"cookieltd\", \"type\": \"food\", \"franchise_location\": [ { \"location_type\": \"town\", \"address_1\": \"5street\" }, { \"location_type\": \"village\", \"address_1\": \"2road\" } ] } }";
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(Company.class).withRootName(
"Company"); // read after the root name
Company company = reader.readValue(json);
System.out.println(company.getFranchise_location().get(0).getAddress_1());
}
public static class Company {
private String name;
private String type;
private List<Location> franchise_location = new ArrayList<Location>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Location> getFranchise_location() {
return franchise_location;
}
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
}
public static class Location {
private String location_type;
private String address_1;
public String getLocation_type() {
return location_type;
}
public void setLocation_type(String location_type) {
this.location_type = location_type;
}
public String getAddress_1() {
return address_1;
}
public void setAddress_1(String address_1) {
this.address_1 = address_1;
}
}
and prints
5street
my solution for JSON is always GSON, you can do some research on that, as long as you have the correct structure of class according to the JSON, it can automatically transfer from JSON to object:
Company company = gson.fromJson(json, Company.class);
GSON is so smart to do the convertion thing!
enjoy GSON !

Java: Best way to store and access a list of objects

What i want to do is store some instances of my class on a list and get a specific instance from that list.
This is an example of a custom class
public class Person
{
private String name;
//Several unrelevant fields here
public Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
//Several unrelevant methods here
}
And this is the code i'm currently using to get one of the instances on the list, that is on the main class.
public class Main
{
private List<Person> people = new ArrayList<Person>();
//More unrelevant fields here
public Person getPerson(String name)
{
for (Person p : people)
if (p.getName().equalsIgnoreCase(name))
return p;
return null;
}
//More unrelevant methods here
}
My question is if there's any other way to write this to increase the performance.
Use a Map whose keys are the names and whose values are the people.
HashMap is case sensitive. If you wanted case-insensitive lookups, you could use a TreeMap. My example demonstrates that people with the same name (case insensitively) overwrite each other.
import java.util.Map;
import java.util.TreeMap;
public class SoMain {
Map<String, Person> nameToPersonMap =
new TreeMap<String, Person>(String.CASE_INSENSITIVE_ORDER);
public static void main(String[] args) {
new SoMain().run(args);
}
private void run(String[] args) {
addPerson(new Person("Jim McDonald", 1));
addPerson(new Person("Jim Mcdonald", 2));
addPerson(new Person("John Smith", 3));
System.out.println("Number of people: "
+ nameToPersonMap.entrySet().size());
System.out.println("Jim McDonald id: "
+ getPerson("Jim McDonald").getPersonId());
System.out.println("John Smith id: "
+ getPerson("john smith").getPersonId());
}
private void addPerson(Person p) {
nameToPersonMap.put(p.getName(), p);
}
private Person getPerson(String name) {
return nameToPersonMap.get(name);
}
public static class Person {
private String name;
private int personId;
public Person(String name, int personId) {
this.name = name;
this.personId = personId;
}
public int getPersonId() {
return personId;
}
public String getName() {
return name;
}
}
}
As Eric mentions, you should use a HashMap, the reasoning for this is because you can look up and add data to one very quickly (on average).
Here is a code example of how to use HashMap using Person.name as the key, this assumes that there is never a person with the same name.
public class Main
{
private HashMap<String, Person> people = new HashMap<String, Person>();
public void addPerson(Person person)
{
people.put(person.getName(), person);
}
public Person getPerson(String name)
{
// get returns null when not found
return people.get(name);
}
}

Jackson JSON Deserialization with Root Element

I am having a question with Jackson that I think should be simple to solve, but it is killing me.
Let's say I have a java POJO class that looks like this (assume Getters and Setters for me):
class User {
private String name;
private Integer age;
}
And I want to deserialize JSON that looks like this into a User object:
{
"user":
{
"name":"Sam Smith",
"age":1
}
}
Jackson is giving me issues because the User is not the first-level object in the JSON. I could obviously make a UserWrapper class that has a single User object and then deserialize using that but I know there must be a more elegant solution.
How should I do this?
edit: this solution only works for jackson < 2.0
For your case there is a simple solution:
You need to annotate your model class with #JsonRootName(value = "user");
You need to configure your mapper with om.configure(Feature.UNWRAP_ROOT_VALUE, true); (as for 1.9) and om.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); (for version 2).
That's it!
#JsonRootName(value = "user")
public static class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(final Integer age) {
this.age = age;
}
#Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
ObjectMapper om = new ObjectMapper();
om.configure(Feature.UNWRAP_ROOT_VALUE, true);
System.out.println(om.readValue("{ \"user\": { \"name\":\"Sam Smith\", \"age\":1 }}", User.class));
this will print:
User [name=Sam Smith, age=1]

Categories

Resources