Java Json program not showing any output - java

What I want to do is , when I navigate to:
http://api.rottentomatoes.com/api/public/v1.0/movies.json?apikey=[your_api_key]&q=Toy+Story+3&page_limit=1
It returns a JSON response. I want to convert it into Java Object. I am using the Jackson library. This is my data class:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class Aman
{
public static class Title
{
private String title;
public String getTitle(){ return title; }
public String setTitle(String s){ return title = s; }
}
private int id;
private int year;
private int total;
public void SetId(int i){ id = i; }
public void SetYear(int y){ year = y; }
public void SetTotal(int t){ total =t; }
public int GetId()
{
return id;
}
public int GetYear()
{
return year;
}
public int GetTotal()
{
return total;
}
#Override
public String toString()
{
return "Movies[total=" +total + "id=" + id + "year=" + year + "]";
}
}
and my mapper class:
import java.io.File;
import java.io.IOException;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import java.net.*;
public class Movie
{
public static void main(String[] args) throws MalformedURLException, URISyntaxException, IOException {
Aman a = new Aman();
ObjectMapper mapper = new ObjectMapper();
try
{
URL RT = new URL("http://api.rottentomatoes.com/api/public/v1.0/movies.json?apikey=gzscv4f8zqse75w94mmp37zz&q=Toy+Story+3&page_limit=1").toURI().toURL();
a = mapper.readValue(RT, Aman.class);
}catch(MalformedURLException u)
{
u.printStackTrace();
}catch(URISyntaxException s)
{
s.printStackTrace();
}catch(IOException i)
{
i.printStackTrace();
}
}
}
It shows me no output whatsoever. Am I doing something wrong?

You have a mix of basic Java programming and JSON mapping errors. First of all, remember to follow lower camel case naming conventions when defining your Java classes. Not only is it a best practice but more importantly, several tools and libraries will not work if you do not follow this pattern (Jackson included).
As far as your JSON mapping errors, the most important thing to keep in mind when mapping JSON to Java objects is that JSON data is effectively a map. It associates keys with values, where the values can be primitives, objects, or arrays (collection). So, given a JSON structure you have to look at the structure of each keys value, then decide wether that value should be represented in Java as a primitive, an object, or as an array of either. No shortcuts to this, you will learn by experience. Here is an example:
{
"total": 1, // maps to primitive, integer
"movies": [ // '[' marks the beginning of an array/collection
{ // '{' marks the beginning of an object
"id": "770672122", // maps to primitive, string
"title": "Toy Story 3",
"year": 2010,
"mpaa_rating": "G",
"runtime": 103,
"release_dates": { // each array object also contains another object
"theater": "2010-06-18",
"dvd": "2010-11-02"
}
}
]
}
When mapping the example JSON above, you need to define a root object that matches the outermost {. Lets call this root object a MovieResponse.
public class MovieResponse {
}
Now, walking down the JSON data, we start to map over all the JSON attributes to Java properties:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MovieResponse {
private Integer total; // map from '"total": 1'
private List<Movie> movies; // map from '"movies": [', remember, its a collection
}
Simple right? But of course, we also need to define a structure for the Movie class. Once again, walking the JSON:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Movie {
private String id; // map from '"id": "770672122"'
private String title; // map from '"title": "Toy Story 3"'
private Integer year;
#JsonProperty("mpaa_rating")
private String mpaaRating;
private Integer runtime;
#JsonProperty("release_dates")
private Release released; // another object mapping!
}
And finally, map the inner-most object that represents release dates:
public class Release {
private String theater;
private String dvd;
}
Thats it, very straightforward. Note the use of #JsonProperty in the Movie class. It allows you to map an attribute from your JSON to a property in Java, with a different name. Also note that constructors/setters/getters were omitted from each of the classes above, for brevity. In your real code you would add them in. Finally, you would map the example JSON to the Java MovieResponse class using the following code:
MovieResponse response = new ObjectMapper().readValue(json, MovieResponse.class);

Related

How to iterate through an Json array with same keys, but different values using Faster xml

I am trying to parse the json array with same key value which looks something like:
Back End Response:"Countries":[{"state":"Queens Land "state":"Tasmania"}].
2.I have created classes to read back end response and mapping the values with faster XML, but only the last value in the array is getting copied, instead of entire array. This is how I created my Data Transfer Object classes.
Now the Test object contains Countries array, but only one of the State value is read. i.e
"Countries":["States":"Tasmania"].
Please excuse me for typos. can some one help, can some one suggest whats wrong with the bellow code..
private Class Test{
List<Countries> countries;
}
private class Countries{
private String States;
}
private class Mapper {
}
In my Mapper class reading the value using faster XML
Assume that your JSON payload is:
{
"Countries": [
{
"state": "Queens Land",
"state": "Tasmania"
}
]
}
According to RFC7159:
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
In your example, you have not unique names and most JSON parsers would skip repeated values and would take only one. So, if you can change backend response, just change it to:
{
"Countries": [
{
"state": "Queens Land"
},
{
"state": "Tasmania"
}
]
}
or
{
"Countries": [
"Queens Land",
"Tasmania"
]
}
But if you can not do that, you need to use Streaming API and implement your custom deserialiser. See below example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Test test = mapper.readValue(jsonFile, Test.class);
System.out.println(test);
}
}
class CountriesJsonDeserializer extends JsonDeserializer<Countries> {
#Override
public Countries deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<String> states = new ArrayList<>();
while (p.nextToken() != JsonToken.END_OBJECT) {
if (p.currentToken() == JsonToken.FIELD_NAME) {
if ("state".equalsIgnoreCase(p.getText())) {
p.nextToken();
states.add(p.getText());
}
}
}
Countries countries = new Countries();
countries.setStates(states);
return countries;
}
}
class Test {
#JsonProperty("Countries")
private List<Countries> countries;
public List<Countries> getCountries() {
return countries;
}
public void setCountries(List<Countries> countries) {
this.countries = countries;
}
#Override
public String toString() {
return "Test{" +
"countries=" + countries +
'}';
}
}
#JsonDeserialize(using = CountriesJsonDeserializer.class)
class Countries {
private List<String> states;
public List<String> getStates() {
return states;
}
public void setStates(List<String> states) {
this.states = states;
}
#Override
public String toString() {
return "Countries{" +
"states=" + states +
'}';
}
}
Above example prints:
Test{countries=[Countries{states=[Queens Land, Tasmania]}]}
See also:
Intro to the Jackson ObjectMapper

How do I create Json Array in Java8 and only print json if I set value for it?

I am trying to generate the following using fasterxml jackson.. but I am stuck. I can't seem to work out how to create arrays.
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false,
"list":[
{
"student":"jim",
"type":"S_A",
"retro":null
},
{
"student":"bob",
"type":"S_A",
"retro":null
}
],
"sort":[]
}
I have two classes. One has the Json properties, and the other is where I print it.
Below class (DynamicJsonHelper) is where I have all the json properties
package com.company.jsonfc;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonPropertyOrder({
"accId",
"groupOf",
"isEnabled"
})
public class DynamicJsonHelper {
public String accId;
public String groupOf;
public List studentList;
#JsonProperty("accId")
public void setAccId(String accId) {
this.accId = accId;
}
#JsonProperty("groupOf")
public void setGroupOf(String groupOf) {
this.groupOf = groupOf;
}
#JsonProperty("isEnabled")
public boolean isEnabled() {
return false;
}
#JsonProperty("studentList")
public List<StudentList> studentList() {
return studentList;
}
}
Student List Class (as suggested)
class StudentList {
String student;
String type;
String retro;
}
And here is class (PrintJson) where I call it.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.company.jsonfc.DynamicJsonHelper;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class PrintJson {
#Test
public void create_json() throws JsonProcessingException {
final JsonNodeFactory factory = JsonNodeFactory.instance;
DynamicJsonHelper dynamicJsonHelper = new DynamicJsonHelper();
String jsonString;
ObjectMapper mapper = new ObjectMapper();
dynamicJsonHelper.setAccId("12345");
jsonString = mapper.writeValueAsString(dynamicJsonHelper);
System.out.println(jsonString);
}
}
This results in printing the following:
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false
"studentList":null
}
1) How do I add the list:[ ... ] array & the sort: [ ] at the end too?
2) In class PrintJson, I don't set value for groupOf but it is still created in Json. How do I set it so if I set value, it is printed.. otherwise it is not included in the json being printed.
I would appreciate if you took my code and gave me example based on it for better understanding please
1) How do I add the list:[ ... ] array & the sort: [ ] at the end too?
Answer: You can create two more classes one for list and one for sort. Now in the class DynamicJsonHelper you can add both of them like you have added accId or isEnabled
and they will be printed. Make sure to add the fields you want in them as instance variables in both of the classes. For example for list you can have a class like:
class StudentList{
String student;
String type;
String retro;
}
Now add a field in your class DynamicJsonHelper as List<StudentList>. Similarly you can do for sort.
2) In class PrintJson, I don't set value for groupOf but it is still
created in Json. How do I set it so if I set value, it is printed..
otherwise it is not included in the json being printed.
Answer: You can either use Object mapper and set it to ignore the null fields during serialization. For eg: mapper.setSerializationInclusion(Include.NON_NULL);
Or you can set it at class level to ignore null values if any. For eg:
#JsonInclude(Include.NON_NULL)
class Test
{
String t;
}
As mentioned in the comments by aBnormaLz above doesn't work if the type is primitive like you have for isEnabled. So consider changing it to Boolean and ensure the same for other fields also.
Edit:
#JsonPropertyOrder({
"accId",
"groupOf",
"isEnabled"
})
#JsonInclude(JsonInclude.Include.NON_NULL)
public class DynamicJsonHelper {
public String accId;
public String groupOf;
public List<Student> studentList;
#JsonProperty("accId")
public void setAccId(String accId) {
this.accId = accId;
}
#JsonProperty("groupOf")
public void setGroupOf(String groupOf) {
this.groupOf = groupOf;
}
#JsonProperty("isEnabled")
public boolean isEnabled() {
return false;
}
#JsonProperty("studentList")
public void setStudentList(List<Student> list) {
this.studentList = list;
}
}
class Student {
private String student;
private String type;
private String retro;
public Student(String student, String type, String retro) {
this.student = student;
this.type = type;
this.retro = retro;
}
public String getStudent() {
return student;
}
public void setStudent(String student) {
this.student = student;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRetro() {
return retro;
}
public void setRetro(String retro) {
this.retro = retro;
}
}
class HelperTest{
public static void main(String[] args) throws JsonProcessingException {
DynamicJsonHelper dynamicJsonHelper = new DynamicJsonHelper();
String jsonString;
ObjectMapper mapper = new ObjectMapper();
dynamicJsonHelper.setAccId("12345");
List<Student> list = Arrays.asList(new Student("s1", "t1", "r1"), new Student("s2", "t2", "r2"));
dynamicJsonHelper.setStudentList(list);
jsonString = mapper.writeValueAsString(dynamicJsonHelper);
System.out.println(jsonString);
}
}
After executing the program the output is as shown below:
{
"accId": "12345",
"isEnabled": false,
"studentList": [
{
"student": "s1",
"type": "t1",
"retro": "r1"
},
{
"student": "s2",
"type": "t2",
"retro": "r2"
}
]
}
You missed a point that you have to have third class and it must have property of type java.util.List or array and name list for your example.
e.g.
public class JsonHolder {
// appropriate Json/Jackson annotations ommitted
private String setAccId;
private String groupOf;
private boolean isEnabled;
private List<DynamicJsonHelper> list;
private String[] sort;
// .. getter/setters ...
}
then you have to create that object and when you created your DynamicJsonHelper put it into list or array.
After all you can serialize JsonHolder object and you will see your java List or Array as JSON array.
UPD: just note that in JSON structure like { ... } is an object and in Java there must be a class for it.
So, starting with JSON structure you posted
{
"setAccId":"12345",
"groupOf":null,
"isEnabled":false,
"list":[
{
"student":"jim",
"type":"S_A",
"retro":null
},
{
"student":"bob",
"type":"S_A",
"retro":null
}
],
"sort":[]
}
it is an object (let name it JsonHolder) with properties named setAcctId, groupOf, isEnabled, list, sort
So you have to have a Java class for it (similar as you did for your DynamicJsonHelper. You can use any #Json annotations you'd like to the same way (I omitted them and left for you). Even you will not have them Jackson or any other JSON serialiser will use property names in Java class by as default.
I mean as example your #JsonProperty("accId")
#JsonProperty("accId")
public void setAccId(String accId)
is not required as long as getter/setter/property named also getAcctId, setAcctId, acctId. Jackson will use that if there is no #JsonProperty annotation.
BTW it is better to do it as you did for code readability. :-)
Per your question:
list and sort properties in JSON are arrays. Jackson parses Java collections classes like List, Set or Arrays to JSON arrays.
Then according to required JSON structure list property is a such collection of DynamicJsonHelper objects you created. in Java class, List or Array is just your choice - use what is more suitable for you. I recommend to use a List rather than Array. Work with arrays in Java is not a good idea. :-)
So far you just created a Java class DynamicJsonHelper only for objects which must be in list property of JsonHolder object. What's left is to create that "JsonHolder" class and give that object to Jackson to serialize it into required JSON structure.
There are bunch of #Json annotations you can use to allow or not null or empty values, change property names, exclude Java class properties to be serialized and so, and so... All is up to you... good luck!

Jackson and deserialisation when you don't know the JSON tag name ahead of time?

I want to use Jackson to deserialise my JSON, from Jira, into a set of POJOs. I have most of what I want working beautifully, now I just have to decode the custom field values.
My input JSON looks like:
{
"expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
"id": "104144",
"self": "https://jira.internal.net/rest/api/2/issue/104144",
"key": "PRJ-524",
"fields": {
"summary": "Redo unit tests to load from existing project",
"components": [],
"customfield_10240": {
"self": "https://jira.internal.net/rest/api/2/customFieldOption/10158",
"value": "Normal",
"id": "10158"
}
}
I can trivially load the summary and components, since I know ahead of time what the name of those JSON elements are, and can define them in my POJO:
#JsonIgnoreProperties({ "expand", "self", "id", })
public class JiraJson
{
private JiraFields fields;
private String key;
public JiraFields getFields()
{
return fields;
}
public String getKey()
{
return key;
}
public void setFields(JiraFields newFields)
{
fields = newFields;
}
public void setKey(String newKey)
{
key = newKey;
}
}
And similarly for JiraFields:
#JsonIgnoreProperties({ "issuetype", "priority", "status" })
public class JiraFields
{
private List<JiraComponent> components;
private String summary;
public List<JiraComponent> getComponents()
{
return components;
}
public String getSummary()
{
return summary;
}
public void setComponents(List<JiraComponent> newComponents)
{
components = newComponents;
}
public void setSummary(String newSummary)
{
summary = newSummary;
}
}
However, the field custom_10240 actually differs depending on which Jira system this is run against, on one it is custom_10240, on another it is custom_10345, so I cannot hard-code this into the POJO. Using another call, it is possible to know at runtime, before the deserialisation starts, what the name of the field is, but this is not possible at compile time.
Assuming that I want to map the value field into a String on JiraFields called Importance, how do I go about doing that? Or perhaps simpler, how to map this Importance onto a JiraCustomField class?
You can use a method annotated with #JsonAnySetter that accepts all properties that are undefined (and not ignored). in case of a Json Object (like the custom field in the question) Jackson passes a Map that contains all the Object properties (it may even contain Map values in case of nested objects). You can now at run time extract whatever properties you want:
#JsonIgnoreProperties({ "issuetype", "priority", "status" })
public class JiraFields
{
private List<JiraComponent> components;
private String summary;
private String importance;
// getter/setter omitted for brevity
#JsonAnySetter
public void setCustomField(String name, Object value) {
System.out.println(name); // will print "customfield_10240"
if (value instanceof Map) { // just to make sure we got a Json Object
Map<String, Object> customfieldMap = (Map<String, Object>)value;
if (customfieldMap.containsKey("value")) { // check if object contains "value" property
setImportance(customfieldMap.get("value").toString());
}
}
}
}
After searching further, I finally found the JsonAlias annotation. This is still defined at compile time, but I had something that I could search further on!
Further searching, and I found PropertyNamingStrategy, which allows you to rename what JSON field name is expected for a setter/field. This has the advantage in that this is done via a method, and the class can be constructed at runtime.
Here is the class that I used to perform this mapping:
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
public final class CustomFieldNamingStrategy
extends PropertyNamingStrategy
{
private static final long serialVersionUID = 8263960285216239177L;
private final Map<String, String> fieldRemapping;
private final Map<String, String> reverseRemapping;
public CustomFieldNamingStrategy(Map<String, String> newFieldRemappings)
{
fieldRemapping = newFieldRemappings;
reverseRemapping = fieldRemapping.entrySet()//
.stream()//
.collect(Collectors.toMap(Map.Entry::getValue,
Map.Entry::getKey));
}
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
{
if (field.getDeclaringClass().getName().equals(JiraFields.class.getName()))
{
return reverseRemapping.getOrDefault(defaultName, defaultName);
}
return defaultName;
}
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,
String defaultName)
{
if (method.getDeclaringClass().getName().equals(JiraFields.class.getName()))
{
return reverseRemapping.getOrDefault(defaultName, defaultName);
}
return defaultName;
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,
String defaultName)
{
if (method.getDeclaringClass().getName().equals(JiraFields.class.getName()))
{
return reverseRemapping.getOrDefault(defaultName, defaultName);
}
return defaultName;
}
}

Jakson polymorphic Enum case

I am glued with some Jackson polymorphic problem.
I work on a web JDR Character Editor personnal project. I use Springboot and try to stuck with the phylosophy. Moreover, I try to make some independent packages, because of study-case for my real work (another springboot project).
With no Jackson configuration, I have no problem for serialization of a Competence. But when I try to get back any modification on the web editor, so when Jackson make a deserialization of a Competence, problems occur with "dependance" property.
Here are my classes:
The one I try to serialize/deserialize:
public class Competence implements Composante, ComposanteTemplate {
public enum Categorie {
APPRENTI,
COMPAGNON
}
private String nom;
private String description;
private Categorie categorie;
private Chapitre chapitre;
private AttributTemplate dependance;
private List sousCompetences = new ArrayList();
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Competence getTemplate() {
return this;
}
public Categorie getCategorie() {
return categorie;
}
public void setCategorie(Categorie categorie) {
this.categorie = categorie;
}
public Chapitre getChapitre() {
return chapitre;
}
public void setChapitre(Chapitre chapitre) {
this.chapitre = chapitre;
}
public AttributTemplate getDependance() {
return dependance;
}
public void setDependance(AttributTemplate dependance) {
this.dependance = dependance;
}
public List getSousCompetences() {
return sousCompetences;
}
public void setSousCompetences(List sousCompetences) {
this.sousCompetences = sousCompetences;
}
public boolean isOuverte() {
return !sousCompetences.isEmpty();
}
}
The superclass of the property I have a problem with:
public interface AttributTemplate extends ComposanteTemplate {}
The two subclasses which could be use for Competence#dependance property:
public enum Carac implements AttributTemplate, Attribut {
FORT(Type.PHYSIQUE),
AGILE(Type.PHYSIQUE),
RESISTANT(Type.PHYSIQUE),
OBSERVATEUR(Type.PHYSIQUE),
SAVANT(Type.MENTALE),
RUSE(Type.MENTALE),
TALENTUEUX(Type.MENTALE),
CHARMEUR(Type.MENTALE);
public enum Type {
PHYSIQUE,
MENTALE
}
public final Type type;
public final String nom = name().toLowerCase();
private String description;
Carac(Type type) {
this.type = type;
}
#Override
public String getNom() { return nom; }
#Override
public String getDescription() { return description; }
#Override
public Carac getTemplate() { return this; }
public void setDescription(String description) { this.description = description; }
}
public enum ArtTemplate implements AttributTemplate {
ART_GUERRIER(2, 1),
ART_ETRANGE(1, 2),
ART_GUILDIEN(1, 1);
public static final String ART_PREFIX = "ART";
public final String nom = name().toLowerCase().replace("_", " ");
public final int nbCaracsPhysiques;
public final int nbCaracsMentales;
private String description;
ArtTemplate(int nbCaracsPhysiques, int nbCaracsMentales) {
this.nbCaracsMentales = nbCaracsMentales;
this.nbCaracsPhysiques = nbCaracsPhysiques;
}
#Override
public String getNom() {
return nom;
}
#Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getNbCaracs() {
return nbCaracsPhysiques + nbCaracsMentales;
}
}
The result json (and then the json I send) is:
{"nom":"Comp_1489746646510","description":"ezbuixnwrclfvmgwdviubcauenzytpzzvumnohwyhpuynxaqhkjdbqygtrmbtlschthovuyoiolkauucwokkfjnaujnufshrjboykuqce","categorie":"APPRENTI","chapitre":"GUERRE","dependance":"ART_ETRANGE","ouverte":false,"sousCompetences":[]}
QUESTION:
I understand that my problem is caused by the abstract relation AttributTemplate, and then when Jackson try to deserialize, he does not know which of Carac or ArtTemplate class to use.
I try to keep unchanged Competence (Competence come from an external jar), so no annotation on this class is possible.
I've tried many of the solutions I found (Jackson 1.5: Polymorphic Type Handling, first steps ) and the only one which has worked was to define a DeserializationProblemHandler
mapper.addHandler(new DeserializationProblemHandler() {
#Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, JsonParser p, String msg) throws IOException {
if (instClass == AttributTemplate.class) {
String name = p.getText();
return !name.startsWith(ArtTemplate.ART_PREFIX) ? Carac.valueOf(name) : ArtTemplate.valueOf(name);
}
return super.handleMissingInstantiator(ctxt, instClass, p, msg);
}
});
But I feel bad with this solution, because I am sure there is an other beautiful one.
So is it possible to configure the mapper in order that he is able to determine which of Carac or ArtTemplate he must use to get AttributTemplate?
EDIT:
I managed to have this:
{"nom":"Comp_1489756873433","description":"kruzueemlwisibshlkotasayfkhdqkqolvhlqgsnntndkpvbmmgklqysabiakaolempmupeyiqaztdcrhwimdksgzybbdzttwnwqjxhfo","categorie":"COMPAGNON","chapitre":"GUERRE","dependance":["mova.ged.perso.inne.Carac","AGILE"],"ouverte":true,"sousCompetences":[...]}
by configuring like this the mapper
abstract class CompetenceMixIn {
private AttributTemplate dependance;
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXISTING_PROPERTY, property="dependance")
#JsonSubTypes({ #JsonSubTypes.Type(value = Carac.class, name = "carac"), #JsonSubTypes.Type(value = ArtTemplate.class, name = "artTemplate") })
public void setDependance(AttributTemplate dependance) {
this.dependance = dependance;
}
}
ObjectMapper mapper = jsonConverter.getObjectMapper();
mapper.addMixIn(Competence.class, CompetenceMixIn.class);
As you could see, I'm still parasited with the array that wrapped dependance value. I would (...)"dependance": "AGILE", (...) not (...)"dependance":["mova.ged.perso.inne.Carac", "AGILE"], (...)
And I don't know what to change in order to have this.
i have been looking into what you are trying to do. Unfortunatelly, I believe there are issues with Enums + inheritance.
I have an alternative solution that you could be using which is to use a custom creator and ignore unknown properties. See the following example:
public class JacksonInheritance {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Competence c = new Competence();
c.desc = "desc";
c.nome = "nome";
c.template = Att1.TEST_Att1;
String test = mapper.writeValueAsString(c);
System.out.println(test);
Competence readValue = mapper.readValue(test, Competence.class);
System.out.println(readValue.template);
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Competence {
private static final Map<String, AttributeTemplate> templates;
static {
templates = new HashMap<>();
Stream.of(Att1.values()).forEach( a -> templates.put(a.name(), a));
Stream.of(Att2.values()).forEach( a -> templates.put(a.name(), a));
}
#JsonProperty
String nome;
#JsonProperty
String desc;
#JsonIgnore
AttributeTemplate template;
#JsonProperty("template_type")
public String getTempl() {
// Here you can do whichever way uou would like to serialise your template. This will be the key
return template.toString();
}
#JsonCreator
public static Competence create(#JsonProperty("template_type") String templateType) {
Competence c = new Competence();
c.template = templates.get(templateType);
return c;
}
}
public static interface AttributeTemplate {
}
public static enum Att1 implements AttributeTemplate {
TEST_Att1;
}
public static enum Att2 implements AttributeTemplate {
TEST2_Att2;
}
}
Here I am detaching the enum logic from the jackson logic and implement my own. This does not require a custom serialisation.
I basically say that I serialise my enum as its value (you can obviously choose which ever properties you would like for this).
My output json then looks as:
{"template_type":"TEST_Att1","nome":"nome","desc":"desc"}
At the return step I now know that the information I need to construct the correct enum template type from the template_type attribute. This is what I can inject into my factory method create.
In the create I can use my statically created map to populate the correct enum into my object. We can just create this map statically since our enums are finite and static.
The beauty of this is also that the generator is only used for creation. Using #JsonIgnoreProperties(ignoreUnknown = true), we can tell jackson to not freak out by all our custom elements in the json. It will simply deserialise any fields it can detect and leave the other ones (since we are using a custom template_type for our enum resolution).
Finally, I am ignoring the actual template in my bean because jackson won't be able to construct that.
I hope that this works for you/helps you. Sorry about the delay.
Reason for not using inheritance:
There seem to be issues with enum + inheritance in jackson. Particularly jackson by default uses reflection and calls the private constructor of the enum for generation. You may be able to get creators to work in a similar way as above though.
The deserialisation expects the template. I am going of the assumption that you do NOT necessarily want to serialise all elements of the enum. This is because the enum name, in my case TEST_Att1 makes the enum unique. There is no need to serialise and send all the different attributes these enums have around. However, Deserialization with #JsonSubTypes for no value - missing property error shows that jackson requires your template field to be at least present. This is a a slight issue, because you want to use an external property for this instead (so why include a null-field as suggested in your json just to make jackson happy)
This may not be the best solution, but I think it is relatively elegant given the restrictions. I hope that helps you,
Artur

How to rename root key in JSON serialization with Jackson

I am using Jackson for JSON serialization of a list of objects.
Here is what I get:
{"ArrayList":[{"id":1,"name":"test name"}]}
But I want this :
{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root name.
Below is my approach to this:
Interface:
public interface MyInterface {
public long getId();
public String getName();
}
Implementation class:
#JsonRootName(value = "rootname")
public class MyImpl implements MyInterface {
private final long id;
private String name;
public MyImpl(final long id,final name) {
this.id = id;
this.name = name;
}
// getters
}
JSon serialization:
public class MySerializer {
public static String serializeList(final List<MyInterface> lists) {
//check for null value.Throw Exception
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
return mapper.writeValueAsString(lists);
}
}
Test:
final List<MyInterface> list = new ArrayList<MyImpl>();
MyImpl item = new MyImpl(1L,"test name");
list.add(item);
final String json = MySerializer.serializeList(list);
System.out.println(json);
Here is what I get:
{"ArrayList":[{"id":1,"name":"test name"}]}
But I want this :
{"rootname":[{"id":1,"name":"test name"}]} // ie showing the string I want as the root name.
I have tried all suggested solutions I could find but failed to achieve my goal. I have looked at:
Jackson : custom collection serialization to JSON
How do I rename the root key of a JSON with Java Jackson?
Jackson : custom collection serialization to JSON
Or am I missing something? I am using jackson 1.9.12 for this. Any help in this regard is welcome.
Well, by default Jackson uses one of two annotations when trying to determine the root name to be displayed for wrapped values - #XmlRootElement or #JsonRootName. It expects this annotation to be on the type being serialized, else it will use the simple name of the type as the root name.
In your case, you are serializing a list, which is why the root name is 'ArrayList' (simple name of the type being serialized). Each element in the list may be of a type annotated with #JsonRootName, but the list itself is not.
When the root value you are trying to wrap is a collection then you need some way of defining the wrap name:
Holder/Wrapper Class
You can create a wrapper class to hold the list, with an annotation to define the desired property name (you only need to use this method when you do not have direct control of the ObjectMapper/JSON transformation process):
class MyInterfaceList {
#JsonProperty("rootname")
private List<MyInterface> list;
public List<MyInterface> getList() {
return list;
}
public void setList(List<MyInterface> list) {
this.list = list;
}
}
final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
MyInterfaceList listHolder = new MyInterfaceList();
listHolder.setList(lists);
final String json = mapper.writeValueAsString(listHolder);
Object Writer
This is the preferable option. Use a configured ObjectWriter instance to generate the JSON. In particular, we are interested in the withRootName method:
final List<MyInterface> lists = new ArrayList<MyInterface>(4);
lists.add(new MyImpl(1L, "test name"));
final ObjectWriter writer = mapper.writer().withRootName("rootName");
final String json = writer.writeValueAsString(lists);
I know, I am late , but I have better approach which don't require Holder/Wrapper Class. It picks root key from annotation.
package com.test;
import com.fasterxml.jackson.annotation.JsonRootName;
#JsonRootName("Products")
public class ProductDTO {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Here is test class:-
package com.test;
import java.io.IOException;
import java.util.ArrayList;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ProductDTOTestCase {
#Test
public void testPersistAndFindById() throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
ProductDTO productDTO = new ProductDTO();
productDTO.setDescription("Product 4 - Test");
ArrayList<ProductDTO> arrayList = new ArrayList<ProductDTO>();
arrayList.add(productDTO);
String rootName = ProductDTO.class.getAnnotation(JsonRootName.class).value();
System.out.println(mapper.writer().withRootName(rootName).writeValueAsString(arrayList));
}
}
It will give following output
{"Products":[{"name":null,"description":"Product 4 - Test"}]}
#JsonTypeName("usuarios")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class UsuarioDT extends ArrayList<Usuario> {
#JsonProperty("rowsAffected")
private Integer afectados;
public Integer getAfectados() {
return afectados;
}
public void setAfectados(Integer afectados) {
this.afectados = afectados;
}
}
You need to use this annotation at the top of the class
#JsonTypeName("rootname")

Categories

Resources