Jackson: Store nested json object as JSONObject - java

This question is duplicate of Link, Code is used as:
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
Car car = mapper.readValue(new File("fileName"), Car.class);
But for settings as given in the link, value comes as {}. How to solve this issue? I want to use nested object as JSONObject.

First of all, you mean JsonObject that is standard from Java EE 7 and not JSONObject that is in Android.
Even in the link you provide they talk about it.
Than, here is my Car example:
package org.mas.examples.jackson.models;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.json.JsonObject;
public class Car {
#JsonProperty(value="id")
private long id;
#JsonProperty(value="name")
private String name;
#JsonIgnore
private JsonObject settings;
public void setId(long id) {
this.id = id;
}
public long getId() {
return this.id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setSettings(JsonObject settings) {
this.settings = settings;
}
public JsonObject getSettings() {
return settings;
}
#JsonAnySetter
public void setJsonProperty(String key, Object value) {
if ("settings".equals(key)) {
System.out.println("Got it " + key + " value: " + value);
/* JsonObject settings = ....; create here your JsonObject */
} else {
System.out.println(key + " value: " + value);
}
}
}
If you run a test like this:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
Car car = objectMapper.readValue(json, Car.class);
System.out.println("Load a car: " + car.getName());
} catch (IOException e) {
e.printStackTrace();
}
You're going to obtain the following result:
Got it settings value: {color=#ff0000}
Load a car: test
So it work just on settings as described in Jackson Annotation doc:
#JsonAnySetter: annotation used for defining a two-argument method as
"any setter", used for deserializing values of otherwise unmapped JSON
properties

Related

Jackson read-only property in method

I need to exposes a property in my json that will be processed in the getter method.
The class:
public class Configuracao{
private String departamento;
public String getDepartamento(){/**getter code**/}
public void setDepartamento(String departamento){/**setter code**/}
public String getDepartamentos(){/***Some logic code***/}
}
The json that got in front: {departamento: "Lote", departamentos: "Lotes"}
Works fine in serialization, but when my front-end post the json back, jackson throws a unrecognized field exception caused by 'departamentos'. How can I tell that I just want to 'departamentos' be serialized by the method value and be ignored in deserialization. I tried #JsonIgnoreProperty, #JsonGetter and #JsonProperty(access = JsonProperty.Access.READ_ONLY) on the method but nothing works.
You can use JsonIgnoreProperties annotation:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.concurrent.ThreadLocalRandom;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
Configuracao c = new Configuracao();
c.setDepartamento("D1");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(c);
System.out.println(json);
System.out.println(mapper.readValue(json, Configuracao.class));
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Configuracao {
private String departamento;
public String getDepartamento() {
return departamento;
}
public void setDepartamento(String departamento) {
this.departamento = departamento;
}
public String getDepartamentos() {
return departamento + " " + ThreadLocalRandom.current().nextDouble();
}
#Override
public String toString() {
return "Configuracao{" +
"departamento='" + departamento + '\'' +
'}';
}
}
Above code prints:
{"departamento":"D1","departamentos":"D1 0.8600092703789755"}
Configuracao{departamento='D1'}
JsonProperty.Access.READ_ONLY should also works:
class Configuracao {
private String departamento;
public String getDepartamento() {
return departamento;
}
public void setDepartamento(String departamento) {
this.departamento = departamento;
}
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getDepartamentos() {
return departamento + " " + ThreadLocalRandom.current().nextDouble();
}
#Override
public String toString() {
return "Configuracao{" +
"departamento='" + departamento + '\'' +
'}';
}
}
with above test works as expected.
If you have more classes like this and fields to ignore, you can disable globally feature DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Everything was tested with version 2.9.9
Just define departamentos property in Configuracao class.
public class Configuracao{
private String departamento;
private String departamentos;
//omitted getter/setter
}

Java JSON Processing

I'm having a hard time processing the below JSON with Java, which is being returned from on an external Ansible playbook:
{"Sample":
{
"tag_description":"abc","tag_category_id":"def","tag_id":"ghi"
},
"Sample1":
{
"tag_description":"jkl","tag_category_id":"mno","tag_id":"pqr"
}
}
I've been able to successfully parse one section of the JSON using a custom deserializer, though it only ever gets the first section. Any ideas are hugely appreciated.
#JsonComponent
public class TagSerializer extends JsonDeserializer<Tag> {
#Override
public Tag deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException,
JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonNode treeNode = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String, JsonNode>> fields = treeNode.fields();
String name = "";
// collect the tag name
Map.Entry<String, JsonNode> entry = fields.next();
name = entry.getKey();
// now that we have the tag name, parse it as a separate JSON object
JsonNode node = entry.getValue();
// get the values from the JSON
String description = node.get("tag_description").asText();
String category_id = node.get("tag_category_id").asText();
String tag_id = node.get("tag_id").asText();
return new Tag(name, category_id, description, tag_id);
}
}
I'm calling the method from a Spring Boot REST API endpoint, and my 'tag' model is a Spring entity:
'Tag' model:
#Entity
#JsonDeserialize(using = TagSerializer.class)
public class Tag {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
private String tag_category_id;
private String tag_description;
private String tag_id;
//JPA requires that a default constructor exists
//for entities
protected Tag() {}
public Tag(String name,
String tag_category_id,
String tag_description,
String tag_id) {
this.name = name;
this.tag_category_id = tag_category_id;
this.tag_description = tag_description;
this.tag_id = tag_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTag_category_id() {
return tag_category_id;
}
public void setTag_category_id(String tag_category_id) {
this.tag_category_id = tag_category_id;
}
public String getTag_description() {
return tag_description;
}
public void setTag_description(String tag_description) {
this.tag_description = tag_description;
}
public String getTag_id() {
return tag_id;
}
public void setTag_id(String tag_id) {
this.tag_id = tag_id;
}
public String toString() {
return "<Tag:[Name: " + this.name + "],[tag_category: "+
this.tag_category_id + "],[tag_description: "+
this.tag_description + "],[tag_id:"+this.tag_id+"]";
}
}
Spring Boot endpoint:
#PostMapping(value="/store", consumes = APPLICATION_JSON_VALUE)
public void tagJson(#RequestBody String json) {
// delete any existing tags
tagRepository.deleteAll();
//lets modify the json to make it look nicer
String modjson = "["+json+"]";
ObjectMapper mapper = new ObjectMapper();
try {
Tag[] tags = mapper.readValue(modjson, Tag[].class);
for (Tag t : tags)
tagRepository.save(t);
} catch (Exception exception) {
exception.printStackTrace();
}
}
If you are using Spring MVC consider explicitly declare desired type when referreing to #RequestBody and let the framework do the job for you
#PostMapping(value="/store", consumes = APPLICATION_JSON_VALUE)
public void tagJson(#RequestBody Map<String, Tag> json) {
// Do not mess with ObjectMapper here, Spring will do the thing for you
}
This isn't a direct answer but a guide in a possible direction, using Gson.
package test;
import java.util.Map;
import com.google.gson.Gson;
public class JsonTest {
public static void main(final String... args) {
new JsonTest().run();
}
public void run() {
final Gson gson = new Gson();
final Map<?, ?> result = gson.fromJson("{" +
" \"Sample\": {" +
" \"tag_description\": \"abc\"," +
" \"tag_category_id\": \"def\"," +
" \"tag_id\": \"ghi\"" +
" }," +
" \"Sample1\": {" +
" \"tag_description\": \"jkl\"," +
" \"tag_category_id\": \"mno\"," +
" \"tag_id\": \"pqr\"" +
" }" +
"}", Map.class);
System.out.println("Map size: " + result.size());
}
}
The resulting size is 2. The map entries are keyed Sample, Sample1, and the values are lists containing the nodes. You can see this using a debugger.

How to implement dynamic partial JSON serialization with Jackson?

I have a POJO with custom setter methods for all properties that track whether the property was explicitly set. The setter stores to fieldNameSet boolean fields and exposes isFieldNameSet getters for those flags. I want Jackson to dynamically serialize the class with only those fields that have isFieldNameSet as true.
Background:
I started writing a custom JsonFilter implementation but it doesn't give any context as to the current object instance being serialized so obviously I can't read the current values of the isFieldNameSet properties.
Quickly hacked from a Jackson example
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ser.FilterProvider;
import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider;
public class JacksonExample {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
User user = createDummyUser();
try {
//Its age here , this is conditional based on your fieldset
SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAllExcept("age");
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", theFilter);
// Convert object to JSON string
String jsonInString = jsonInString = mapper.writer(filters).writeValueAsString(user);
System.out.println(jsonInString);
// Convert object to JSON string and pretty print
//System.out.println(jsonInString);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static User createDummyUser() {
User user = new User();
user.setName("mkyong");
user.setAge(33);
List<String> msg = new ArrayList<>();
msg.add("hello jackson 1");
msg.add("hello jackson 2");
msg.add("hello jackson 3");
user.setMessages(msg);
return user;
}
}
package org.soproject;
import java.util.List;
import org.codehaus.jackson.map.annotate.JsonFilter;
#JsonFilter("myFilter")
public class User {
private String name;
private int age;
private List<String> messages;
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> getMessages() {
return messages;
}
public void setMessages(List<String> messages) {
this.messages = messages;
}
// getters and setters
}
Ignores age as you see :
{"name":"mkyong","messages":["hello jackson 1","hello jackson 2","hello jackson 3"]}
Note jackson source is from : https://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/

How can I unwrap a specific field in a JSON using Jackson?

I have a JSON payload that looks like this:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"primary_image": {
"id": 247,
"zoom_url": "www.site.com/in_123__14581.1393831046.1280.1280.jpg",
"thumbnail_url": "www.site.com/in_123__14581.1393831046.220.290.jpg",
"standard_url": "www.site.com/in_123__14581.1393831046.386.513.jpg",
"tiny_url": "www.site.com/in_123__14581.1393831046.44.58.jpg"
}
}
Can I unwrap a specific field and discard all the others? In other words, can I bind this directly to a POJO like this:
public class Product {
private Integer id;
private String name;
private String standardUrl;
}
There are lots of ways. Do you need to deserialize, serialize or both?
One way to deserialize would be to use a creator method that takes the image as a tree node:
public static class Product {
private Integer id;
private String name;
private String standardUrl;
public Product(#JsonProperty("id") Integer id,
#JsonProperty("name") String name,
#JsonProperty("primary_image") JsonNode primaryImage) {
this.id = id;
this.name = name;
this.standardUrl = primaryImage.path("standard_url").asText();
}
}
The creator doesn't have to be a constructor, you could have a static method that is only used for Jackson deserialization.
You'd have to define a custom serializer to reserialize this, though (e.g. a StdDelegatingSerializer and a converter to wrap the string back up as an ObjectNode)
There are different ways to skin this cat, I hope you can use Jackson 2 for this, since it offers great ways to deserialize Json data, one of my favorites deserialization features is the one I'll show you here (using Builder Pattern) because allows you to validate instances when they are being constructed (or make them immutable!). For you this would look like this:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.Map;
#JsonDeserialize(builder = Product.Builder.class)
public class Product {
private Integer id;
private String name;
private String standardUrl;
private Product(Builder builder) {
//Here you can make validations for your new instance.
this.id = builder.id;
this.name = builder.name;
//Here you have access to the primaryImage map in case you want to add new properties later.
this.standardUrl = builder.primaryImage.get("standard_url");
}
#Override
public String toString() {
return String.format("id [%d], name [%s], standardUrl [%s].", id, name, standardUrl);
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Builder {
private Integer id;
private String name;
private Map<String, String> primaryImage;
public Builder withId(Integer id) {
this.id = id;
return this;
}
public Builder withName(String name) {
this.name = name;
return this;
}
#JsonProperty("primary_image")
public Builder withPrimaryImage(Map<String, String> primaryImage) {
this.primaryImage = primaryImage;
return this;
}
public Product build() {
return new Product(this);
}
}
}
To test it I created this class:
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class Test {
public static void main(String[] args) {
String serialized = "{" +
" \"id\": 32," +
" \"name\": \"[Sample] Tomorrow is today, Red printed scarf\"," +
" \"primary_image\": {" +
" \"id\": 247," +
" \"zoom_url\": \"www.site.com/in_123__14581.1393831046.1280.1280.jpg\"," +
" \"thumbnail_url\": \"www.site.com/in_123__14581.1393831046.220.290.jpg\"," +
" \"standard_url\": \"www.site.com/in_123__14581.1393831046.386.513.jpg\"," +
" \"tiny_url\": \"www.site.com/in_123__14581.1393831046.44.58.jpg\"" +
" }" +
" }";
ObjectMapper objectMapper = new ObjectMapper();
try {
Product deserialized = objectMapper.readValue(serialized, Product.class);
System.out.print(deserialized.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
The output is (using the override toString() method in Product:
id [32], name [[Sample] Tomorrow is today, Red printed scarf], standardUrl [www.site.com/in_123__14581.1393831046.386.513.jpg].
There are two ways to get the response you required. For both methods, we are going to use JsonView.
Create two types of JsonView:
public interface JViews {
public static class Public { }
public static class Product extends Public { }
}
First method
#JsonView(JViews.Public.class)
public class Product {
private Integer id;
private String name;
#JsonIgnore
private Image primaryImage;
#JsonView(JViews.Product.class)
public String getStandardUrl{
return this.primaryImage.getStandardUrl();
}
}
Second way
Using Jackson's #JsonView and #JsonUnwrapped together.
#JsonView(JViews.Public.class)
public class Product {
private Integer id;
private String name;
#JsonUnwrapped
private Image primaryImage;
}
public class Image {
private String zoomUrl;
#JsonView(JViews.Product.class)
private String standardUrl;
}
#JsonUnwrapped annotation flattens your nested object into Product object. And JsonView is used to filter accessible fields. In this case, only standardUrl field is accessible for Product view, and the result is expected to be:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"standard_url": "url"
}
If you flatten your nested object without using Views, the result will look like:
{
"id": 32,
"name": "[Sample] Tomorrow is today, Red printed scarf",
"id":1,
"standard_url": "url",
"zoom_url":"",
...
}
Jackson provided #JsonUnwrapped annotation.
See below link:
http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html

JSON parsing through GSON returns null values

I am trying to read the values of a JSON output.
This is the JSON output:
{"nameOfSummoner":{"id":56529189,"name":"test","profileIconId":550,"summonerLevel":30,"revisionDate":1422110739000}}
And with the following code I am trying to read it:
final Connector connector = new Connector();
String response = connector.connect("link"); // (Returns a String value of the JSON)
final Gson gson = new Gson();
final Summoner summoner = gson.fromJson(response, Summoner.class); //Summoner is a model class
System.out.println(summoner);
Summoner class:
public class Summoner {
private String name;
private long profileIconId;
private long summonerLevel;
private long revisionDate;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public long getProfileIconId() {
return profileIconId;
}
public void setProfileIconId(final long profileIconId) {
this.profileIconId = profileIconId;
}
public long getSummonerLevel() {
return summonerLevel;
}
public void setSummonerLevel(final long summonerLevel) {
this.summonerLevel = summonerLevel;
}
public long getRevisionDate() {
return revisionDate;
}
public void setRevisionDate
(long revisionDate) {
this.revisionDate = revisionDate;
}
#Override
public String toString() {
return "Summoner{" +
"name='" + name + '\'' +
", profileIconId=" + profileIconId +
", summonerLevel=" + summonerLevel +
", revisionDate=" + revisionDate +
'}';
}
}
And I get the following output on the console:
Summoner{name='null', profileIconId=0, summonerLevel=0, revisionDate=0}
I have sadly no idea why this happens. Any help I get is appreciated. I am fairly sure it has to do with the JSON output that "nameOfSummoner" is on top and maybe that's why it does not read what is below.
As mentioned by #PeterMmm , your input is a map with 1 key-value pair.
You need to Create another POJO with Summoner object as attribute:
public class Sample {
private Summoner nameOfSummoner;
//getters and setters
}
and then try parsing. Or, you could create a Map and parse.
Map<String, Summoner> responseObj = new HashMap<String, Summoner>();
responseObj= gson.fromJson(response, responseObj.class);
Summoner obj = responseObj.get("nameOfSummoner");
You will also need to have "id" attribute in Summoner class I believe, else gson will throw an exception.

Categories