Handle java inheritance in json - json

My class is as below:
class A
{
private B b;
private String id;
//setter and getter
}
class B
{
private String name;
//setter and getter
}
class C extends B
{
private String email;
//setter and getter
}
My Json is as below:
{
"name":"Sample",
"id":2,
"email":"support#abd.com"
}
Now when I try to deserialise my json into a java object, I get:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "email"
Please let me know how I could solve this.

You need to make sure that you are handling deserializing the shared nodes in the base class, and the nodes specific to your class b/c in their own respective classes. If you are handling deserialization in your top level class, it won't recognize a unique property in an inheriting class.
It's difficult to see where you are going wrong without seeing your individual classes.

Related

Angular casting http get response but not nested property

I am returning a list of products of type ListProd in my Spring Boot controller, and such object has a nested property of type List<Product> ... something like this:
public class Product implements Serializable {
int codProdct;
String dsc;
public Product(){};
// plus getters and setters (omitted for simplicity)
}
public class ListProd implements Serializable {
int codList;
List<Product> products;
public ListProd(){};
// also here getters and setters
}
and in my angular project I have the next analogoust entities:
export class Product {
public codProduct:number;
public dsc:string;
constructor(){
codProduct=null; //just for completness
dsc='';
}
}
export class ListProd {
public codList:number;
public products:Product[];
constructor(){
this.codList=null; //just for completness
this.products=null;
}
}
I'm sending a ListProd object through my Spring controller and Angular gets such an object correctly and assigns the type also correctly:
return this.http.get<ListProd>(this.apiUrl + "/getListProds", httpOptions);
but ... it is only casting correctly the parent object, when I check the type of the 'products' object (which is recived correctly, only untyped) y get type: Object
I tried to make it a Products[] in my ListProd class in java, but i get the same result. I don't know much about the 'magic' Angular does to assign those types properly, am I missing something important here? Is there a chance to force the casting on the Angular side?
Thanks in advance.
Well, if ListProd is supposed to hold array of Product[] there is a typo here.
public products:Product[];
Not knowing full extent why you use class over an interface I would advise you also to go over this. Maybe there is a reason but for being able to strong type simple interface would be better. https://www.javatpoint.com/typescript-class-vs-interface

JSON to POJO conversion using Jackson & JsonPath

I have a data scenario, where I want to populate an attribute in my POJO class using the attribute of a nested object. The below is just an example, but I have many such nested variables.
For example my java class is as follow
public class Book{
private String title;
private String author;
private String isbn;
...
}
And my json response that I need to deserialize is as follow
{
"title":"the jungle book",
"author":"Rudyard Kipling",
"code":{
"isbn":"1616416920"
}
}
So the attribute isbn in my java class, needs to be populated using the nested field isbn inside the code object.
I tried to use the #JsonProperty as follow
#JsonProperty(value="code.isbn")
private String isbn;
But it still set the attribute isbn to null inside my java POJO class.
Can someone please suggest how can I do this.
Thanking you in advance.
you can write custom code to parse the json to create Book object having isbn value set by parsing nested object.
or
for the given json, you need to define an object say "Code"
public class Book {
#JsonProperty(value="title")
private String title;
#JsonProperty(value="author")
private String author;
#JsonProperty(value="code")
private Code code;
}
public class Code {
#JsonProperty(value="isbn")
public String isbn;
}

REST: how to serialize a java object to JSON in a "shallow" way?

Suppose I have the following JPA entities:
#Entity
public class Inner {
#Id private Long id;
private String name;
// getters/setters
}
#Entity
public class Outer {
#Id private Long id;
private String name;
#ManyToOne private Inner inner;
// getters/setters
}
Both Spring and java EE have REST implementations with default serializers which will marshall the entities to/from JSON without further coding. But when converting Outer to JSON, both Spring and EE nest a full copy of Inner within it:
// Outer
{
"id": "1234",
"name": "MyOuterName",
"inner": {
"id": "4321",
"name": "MyInnerName"
}
}
This is correct behavior but problematic for my web services, since the object graphs can get deep/complex and can contain circular references. Is there any way to configure the supplied marshaller to marshall the POJOs/entities in a "shallow" way instead without having to create a custom JSON serializer for each one? One custom serializer that works on all entities would be fine. I'd ideally like something like this:
// Outer
{
"id": "1234",
"name": "MyOuterName",
"innerId": "4321"
}
I'd also like it to "unmarshall" the JSON back into the equivalent java object. Bonus kudos if the solution works with both Spring and java EE. Thanks!
After many problems I give reason to Cássio Mazzochi Molin saying that "the use of entities persistence in your REST API can not be a good idea"
I would do that the business layer transform persistence entities to DTO.
You can do this very easily with libraries like mapstruct
If you still want to continue with this bad practice you can use jackson and customize your jackson mapper
To unscramble complex object graphs using jaxb #XmlID and #XmlIDREF is made for.
public class JSONTestCase {
#XmlRootElement
public static final class Entity {
private String id;
private String someInfo;
private DetailEntity detail;
#XmlIDREF
private DetailEntity detailAgain;
public Entity(String id, String someInfo, DetailEntity detail) {
this.id = id;
this.someInfo = someInfo;
this.detail = detail;
this.detailAgain = detail;
}
// default constructor, getters, setters
}
public static final class DetailEntity {
#XmlID
private String id;
private String someDetailInfo;
// constructors, getters, setters
}
#Test
public void testMarshalling() throws JAXBException {
Entity e = new Entity( "42", "info", new DetailEntity("47","detailInfo") );
JAXBContext context = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(new Class[]{Entity.class}, null);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
m.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
m.marshal(e, System.out);
}
}
This will result in the following json-fragment
{
"detailAgain" : "47",
"detail" : {
"id" : "47",
"someDetailInfo" : "detailInfo"
},
"id" : "42",
"someInfo" : "info"
}
Unmarshalling of this json will ensure that detail and detailAgain are the same instances.
The two annotations are part of jaxb, so it will work in Spring as well as in java EE. Marshalling to json is not part of the standard, so i use moxy in the example.
Update
Explicitly using moxy is not neccessary in a JAX-RS Resource. The following snipped perfectly runs on a java-EE-7 container (glassfish 4.1.1) and results in the above json-fragment:
#Stateless
#Path("/entities")
public class EntityResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Entity getEntity() {
return new Entity( "42", "info", new DetailEntity("47","detailInfo") );
}
}
I had the same problem and ended up using jackson annotations on my Entities to control the serialization:
What you need is #JsonIdentityReference(alwaysAsId=true) to instruct the bean serializer that this reference should be only an ID. You can see an example on my repo:
https://github.com/sashokbg/company-rest-service/blob/master/src/main/java/bg/alexander/model/Order.java
#OneToMany(mappedBy="order", fetch=FetchType.EAGER)
#JsonIdentityReference(alwaysAsId=true) // otherwise first ref as POJO, others as id
private Set<OrderDetail> orderDetails;
If you want a full control of how your entities are represented as JSON, you can use JsonView to define which field is serialized related to your view.
#JsonView(Views.Public.class)
public int id;
#JsonView(Views.Public.class)
public String itemName;
#JsonView(Views.Internal.class)
public String ownerName;
http://www.baeldung.com/jackson-json-view-annotation
Cheers !
for this problem There are two solutions.
1-using jackson json view
2- Createing two mapping classe for innner entity. one of them includes custom fields and another one includes all fields ...
i think jackson json view is better solution ...
Go through the FLEXJSON library to smartly include/exclude nested class hierarchy while serializing Java objects.
Examples for flexjson.JSONSerializer presented here
You can detach the JPA entity before serialization, if you use lazyloading it's avoid to load sub objects.
Another way, but is depend of the JSON serializer API, you can use "transient" or specifics annotation.
Why does JPA have a #Transient annotation?
A bad way is to use tool like dozer to copy JPA object in another class with only the properties need for json (but it works... little overhead of memory, CPU and time...)
#Entity
public class Outer {
#Id private Long id;
private String name;
#ManyToOne private Inner inner;
//load manually inner.id
private final Long innerId;
// getters/setters
}

Unexpected duplicate key error using #JsonTypeInfo property

I have a simple hierarchy of data objects, which have to be converted to JSON format. Like this:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "documentType")
#JsonSubTypes({#Type(TranscriptionDocument.class), #Type(ArchiveDocument.class)})
public class Document{
private String documentType;
//other fields, getters/setters
}
#JsonTypeName("ARCHIVE")
public class ArchiveDocument extends Document { ... }
#JsonTypeName("TRANSCRIPTIONS")
public class TranscriptionDocument extends Document { ... }
Upon JSON parsing I encounter errors like this one:
Unexpected duplicate key:documentType at position 339. , because in the generated JSON there are actually two documentType fields.
What should be changed to make JsonTypeName value appear in documentType field, without an error (eg replacing the other value)?
Jackson version is 2.2
Your code doesn't show it, but I bet you have a getter in your Document class for the documentType property. You should annotate this getter with #JsonIgnore like so:
#JsonIgnore
public String getDocumentType() {
return documentType;
}
There is an implicit documentType property associated with each subclass, so having the same property in the parent class causes it to be serialized twice.
Another option would be to remove the getter altogether, but I assume you might need it for some business logic, so the #JsonIgnore annotation might be the best option.

Jackson deserialization when POJO does not match JSON structure

I have some json :
{
key: "CORE-19",
fields: { summary: "iblah" }
}
I want to pack it into a POJO that looks more like:
#JsonIgnoreProperties(ignoreUnknown=true)
public class JiraIssue
{
private String mKey;
private String mSummary;
public String getKey(){ return(mKey);}
public void setKey(String inKey){mKey = inKey;}
public String getSummary(){return(mSummary);}
public void setSummary(String summary){ mSummary = summary; }
}
So basically I don't want to create a 'Fields' object as it is a bit superfluous for my needs. However I really can't see any way in Jackson to tell it that the 'summary' property actually comes from the 'fields' property. Is this possible?
Serialization of this class is not a concern, it will only ever be used for Deserialization. I have no control over the JSON format as it is coming from an external source (and the above is just a snippet). Also I'm actually using Jackson with Jersey.
Thanks!
There is actually an open issue for this kind of structural change. There is no way as of now to do that easily with annotation only without modifying your class. What you could do instead is handle the "fields" property as a "false" property, by adding the following method:
public void setFields(Map<String, String> fields) {
setSummary(fields.get("summary"));
}
This way you "unwrap" the property yourself.
Try:
#JsonProperty("fields.summary")
private String mSummary;