I am receiving from API json like that:
{
"channel":"masta",
"startTime":1427673600000,
"endTime":1427760000000,
"totalUniques":1,
"totalViewtime":1927,
"totalViews":13,
"totalCountries":1,
"countries":{
"US":{
"uniques":1,
"views":13,
"viewtime":1927
}
}
}
Now I want to deserialize it to class, so this class(Stats) will have fields like channel, startTime and so on.
But how to handle countries property?
I thought about making class Countries but not sure about that cause it's have "US" as property name. Not "country": "US". And what's more it has own parameters. How to deserialize it?
Mostly I am using ObjectMapper object.readValue(jsonString) to do that but don't know how to handle 'countries'. In example is just one country 'US' but can be more.
Declare Country class:
public class Country {
private int uniques;
private int views;
private int viewtime;
public int getUniques() {
return uniques;
}
public void setUniques(int uniques) {
this.uniques = uniques;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
public int getViewtime() {
return viewtime;
}
public void setViewtime(int viewtime) {
this.viewtime = viewtime;
}
}
In your Stats class you should declare countries as map of Country objects:
public class Stats {
private String channel;
private Long startTime;
private Long endTime;
private int totalUniques;
private int totalViewtime;
private int totalViews;
private int totalCountries;
...
private Map<String, Country> countries;
public Map<String, Country> getCountries() {
return countries;
}
public void setCountries(Map<String, Country> countries) {
this.countries = countries;
}
}
Now you can deserialize you object:
ObjectMapper mapper = new ObjectMapper();
Stats stats = mapper.readValue(jsonString, Stats.class);
After deserialization your Stack object will get map with one Country object with key "US".
Basically, you need to define a POJO like ViewInfo that has the attributes like channel, startTime, endTime and so on. countries is also another attribute, but it isn't a primitive, it's like a map with country code as the key and Country as another POJO (which has attributes like uniques, views and so on). This way, you should be able to deserialize the json into this pojo.
Related
I want to program a restful API and annotate my data with schema.org. For this I wanted to use Jackson-Jsonld. Annotating simple objects with jackson-jsonld were no problem, but complex ones with nested objects got me stucked. In my jsonld the simple attributes like id, name got anntotated but the nested location not.
I read about Serialization and that it should help in order to get the second object. However, after implementing my serialization part it seems that the serialization did not changed anything.
Here is my sample output, the type for location should be PostalAddress however the type is missing:
{"#context":
{"uri":"http://schema.org/url","name":"http://schema.org/name","location":"http://schema.org/location"},
"#type":"http://schema.org/Organization",
"uri":"http://localhost:8080/kangarooEvents/venue/12",
"name":"Joondalup Library - Ground Floor Meeting Room",
"location":{
"address":"102 Boas Avenue",
"city":"Joondalup",
"zip":"6027",
"country":"Australia",
"state":"WA"},
"#id":12}
I want to annotate an organization which has a single location:
#JsonldType("http://schema.org/Organization")
public class Venue {
#JsonldId
private Integer id;
#JsonldProperty("http://schema.org/url")
private String uri;
#JsonldProperty("http://schema.org/name")
private String name;
#JsonSerialize(using = CostumLocationSerializer.class)
#JsonldProperty("http://schema.org/location")
private Location location;
Location:
#JsonldType("http://schema.org/PostalAddress")
public class Location {
#JsonldProperty("http://schema.org/streetAddress")
private String address;
#JsonldProperty("http://schema.org/addressLocality")
private String city;
#JsonldProperty("http://schema.org/addressRegion")
private String state;
#JsonldProperty("http://schema.org/addressRegion")
private String country;
#JsonldProperty("http://schema.org/postalCode")
private String zipcode;
Serialization:
public class CostumLocationSerializer extends StdSerializer<Location> {
private ObjectMapper mapper = new ObjectMapper();
public CostumLocationSerializer(){
this( null);
}
protected CostumLocationSerializer(Class<Location> t) {
super(t);
}
#Override
public void serialize(Location location, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("address", location.getAddress());
jsonGenerator.writeStringField("city", location.getCity());
jsonGenerator.writeStringField("zip", location.getZipcode());
jsonGenerator.writeStringField("country", location.getCountry());
jsonGenerator.writeStringField("state", location.getState());
jsonGenerator.writeEndObject();
String serialized = mapper.writeValueAsString(location);
}
}
I think that my problem might be in the serialization but I can not figure it out. Maybe someone annotated nested obj. and can tell me what my problem is.
Just skip the jackson-jsonld part and do it manually
Create JSON - just introduce a field for type and id into your java classes.
Create a JSON-LD context - map your id and type fields in an additional #context object
Combine context and data - e.g. just add your #context object after your 'normal' json serialization using standard jackson API.
Example
#Test
public void createJsonFromPojo() throws Exception {
ObjectMapper mapper=new ObjectMapper();
// Create object structure
Venue venue = new Venue();
venue.location = new Location();
venue.id="12";
venue.uri="http://localhost:8080/kangarooEvents/venue/12";
venue.name="Joondalup Library - Ground Floor Meeting Room";
venue.location.address="102 Boas Avenue";
venue.location.city="Joondalup";
venue.location.state="WA";
venue.location.country="Australia";
venue.location.zipcode="6027";
//1. Create JSON
ObjectNode myData = mapper.valueToTree(venue);
//2. Create a JSON-LD context
ArrayNode context = mapper.createArrayNode();
context.add("http://schema.org/");
ObjectNode myContext=mapper.createObjectNode();
myContext.put("id", "#id");
myContext.put("type", "#type");
context.add(myContext);
//3. Combine context and data
myData.set("#context",context);
//4. Print
StringWriter w = new StringWriter();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true).writeValue(w, myData);
String result= w.toString();
System.out.println(result);
}
public class Venue {
public final String type = "http://schema.org/Organization";
public String id;
public String uri;
public String name;
public Location location;
}
public class Location {
public final String type = "http://schema.org/PostalAddress";
public String address;
public String city;
public String state;
public String country;
public String zipcode;
}
Gives you
{
"#context": [
"http://schema.org/",
{
"id": "#id",
"type":"#type"
}
],
"uri":"http://localhost:8080/kangarooEvents/venue/12",
"name":"Joondalup Library - Ground Floor Meeting Room",
"location":{
"address":"102 Boas Avenue",
"city":"Joondalup",
"zip":"6027",
"country":"Australia",
"state":"WA",
"type":"http://schema.org/PostalAddress"
},
"id":"12",
"type":"http://schema.org/Organization"
}
View Example in Playground
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
I have a JSON array like as shown below which I need to serialize it to my class. I am using Jackson in my project.
[
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc1",
"clientValue": {}
},
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc2",
"clientValue": {}
}
]
In above JSON array, clientValue will have another JSON object in it. How can I serialize my above JSON array into my java class using Jackson?
public class DataRequest {
#JsonProperty("clientId")
private String clientId;
#JsonProperty("clientName")
private int clientName;
#JsonProperty("clientKey")
private String clientKey;
#JsonProperty("clientValue")
private Map<String, Object> clientValue;
//getters and setters
}
I have not used jackson before so I am not sure how can I use it to serialize my JSON array into Java objects? I am using jackson annotation here to serialize stuff but not sure what will be my next step?
You can create a utility function shown below. You may want to change the Deserialization feature based on your business needs. In my case, I did not want to fail on unknown properties => (FAIL_ON_UNKNOWN_PROPERTIES, false)
static <T> T mapJson(String body,
com.fasterxml.jackson.core.type.TypeReference<T> reference) {
T model = null;
if(body == null) {
return model;
}
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
try {
model = mapper.readValue(body, reference);
} catch (IOException e) {
//TODO: log error and handle accordingly
}
return model;
}
You can call it using similar approach as shown below:
mapJson(clientValueJsonString,
new com.fasterxml.jackson.core.type.TypeReference<List<DataRequest>>(){});
You can try #JsonAnyGetter and #JsonAnySetter annotations with an inner class object. Also clientName should have type String, not int.
public class DataRequest {
private String clientId;
private String clientName;
private String clientKey;
private ClientValue clientValue;
//getters and setters
}
public class ClientValue {
private Map<String, String> properties;
#JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
#JsonAnyGetter
public Map<String,String> getProperties() {
return properties;
}
}
I have a JSON object that looks like this
{
id:int,
tags: [
"string",
"string"
],
images: {
waveform_l:"url_to_image",
waveform_m:"url_to_image",
spectral_m:"url_to_image",
spectral_l:"url_to_image"
}
}
I'm trying to use retrofit to parse the JSON and create the interface. The problem that I have is that I get a null for the images urls. Everything else works, I am able to retrieve the id, the tags, but when I try to get the images they are all null.
I have a sound pojo that looks like this:
public class Sound {
private Integer id;
private List<String> tags = new ArrayList<String>();
private Images images;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Images getImages() {
return images;
}
public void setImages(Images images) {
this.images = images;
}
... setters and getter for tags as well
}
and I have a Images pojo that looks like this:
public class Images {
private String waveformL;
private String waveformM;
private String spectralM;
private String spectralL;
public String getWaveformL() {
return waveformL;
}
public void setWaveformL(String waveformL) {
this.waveformL = waveformL;
}
public String getWaveformM() {
return waveformM;
}
public void setWaveformM(String waveformM) {
this.waveformM = waveformM;
}
public String getSpectralM() {
return spectralM;
}
public void setSpectralM(String spectralM) {
this.spectralM = spectralM;
}
public String getSpectralL() {
return spectralL;
}
public void setSpectralL(String spectralL) {
this.spectralL = spectralL;
}
}
Whenever I try to call images.getWaveformM() it gives me a null pointer. Any ideas?
#SerializedName can also be used to solve this. It allows you to match the expected JSON format without having to declare your Class variable exactly the same way.
public class Images {
#SerializedName("waveform_l")
private String waveformL;
#SerializedName("waveform_m")
private String waveformM;
#SerializedName("spectral_m")
private String spectralM;
#SerializedName("spectral_l")
private String spectralL;
...
}
If the only differences from the JSON to your class variables are the snake/camel case then perhaps #njzk2 answer works better but in cases where there's more differences outside those bounds then #SerializeName can be your friend.
You possibly need this part:
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) will allow gson to automatically transform the snake case into camel case.
public class Images {
private String waveform_l;
private String waveform_m;
private String spectral_m;
private String spectral_m;
}
Key name should be same in model as in json other wise it won't recognise it else you haven't define it at GsonBuilder creation.Generate the getter setter for the same and you will be good to go
I have JSON, with differents levels field, so I want to convert to a single JSON with fields with one level for example:
{
"prop1":"value1",
"prob2":"value2",
"prop3": {
"prop4":"value4",
"prop5":"value5"
}
... many level fields
}
result
{
"prop1":"value1",
"prop2":"value2",
"prop4":"value4",
"prop5":"value5"
.......
}
I'm using Jackson with annotation #JsonProperty("field"), I haven't problem wih fields of first level , but I donĀ“t know how to access field where to into more inside JSON , for this example are prop4 and prop5.
JsonUnwrapped is the annotation to use, it even works for multi-level nesting. For example:
#RunWith(JUnit4.class)
public class Sample {
#Test
public void testName() throws Exception {
SampleClass sample = new SampleClass("value1", "value2", new SubClass("value4", "value5", new SubSubClass("value7")));
new ObjectMapper().writeValue(System.out, sample);
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SampleClass {
private String prop1;
private String prop2;
#JsonUnwrapped
private SubClass prop3;
public SampleClass(String prop1, String prop2, SubClass prop3) {
this.prop1 = prop1;
this.prop2 = prop2;
this.prop3 = prop3;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubClass {
private String prop4;
private String prop5;
#JsonUnwrapped
private SubSubClass prop6;
public SubClass(String prop4, String prop5, SubSubClass prop6) {
this.prop4 = prop4;
this.prop5 = prop5;
this.prop6 = prop6;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubSubClass{
private String prop7;
public SubSubClass(String prop7) {
this.prop7 = prop7;
}
}
}
will generate
{"prop1":"value1","prop2":"value2","prop4":"value4","prop5":"value5","prop7":"value7"}
Try implementing the #JsonUnwrapped annotation. More information at http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html