Jackson XML: nested field deserialization - java

I have the following xml
<MyPojo>
<name>Jason</name>
<age>25</age>
<meta>
<occupation>Engineer</occupation>
</meta>
</MyPojo>
I need to deserialize it to the following POJO:
public class MyPojo {
private String name;
private int age;
private String occupation;
}
The problem here is that occupation is wrapped within meta element

You need one more object:
public class MyPojo {
private String name;
private int age;
private Meta meta;
}
public class Meta{
private String occupation;
}

My idea is to replace occupation with an own class. Something like myMeta or whatever you want to call it(be aware in your case like the xml says: meta). This class should cotain the field occupation:
public class Meta
{
private String occupation;
}
After that you only have to add a new field of your new class e.g. myMeta to myPojo. Something like this:
public class MyPojo
{
private String name;
private int age;
private Meta meta;
}
this should avoid
that occupation is wrapped within meta element
Hope that helps!

Related

PMD Maven Plugin ignores exclude properties Values for inner class

I'm using PMD v3.12 in my current project.
I have a POJO with some properties:
public class Person {
private String name;
private String surname;
private String state;
private String zip;
private String town;
private Integer id;
private LocalDate dateOfBirth;
private String locationOfBirth;
// GETTERS & SETTERS, HASHCODE, TOSTRING
.
.
.
}
I added an inner Builder class for this Pojo:
public class Person {
private String name;
private String surname;
private String state;
private String zip;
private String town;
private Integer id;
private LocalDate dateOfBirth;
private String locationOfBirth;
// GETTERS & SETTERS, HASHCODE, TOSTRING
...
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final Person person;
public Builder() {
this.person = new Person();
}
public Builder name(String name) {
this.person.setName(name);
return this;
}
// Other Methods to build an instance
public Person build() {
return this.person;
}
}
}
Now when PMD is executed, it tells me that the class Person violates TooManyMethods and should be refactored, pointing to the line number, where the inner class begins.
Since i don't want to refactor my Builder, i tried adding an exclusion to the exclude-pmd.properties file, but it doesn't work:
org.my.path.to.my.file.Person=TooManyMethods
I also tried:
org.my.path.to.my.file.Person.Builder=TooManyMethods
org.my.path.to.my.file.Builder=TooManyMethods
and i tired adding #SuppressWarnings("PMD") on both, the parent class and the inner class, but nothing worked.
I am aware that lombok exists, but in my current company it's not allowed, also changing the settings of PMD to allow more Methods to a class is not possible.
Any help is appreciated, thanks in advance!

When does Jackson require no-arg constructor for deserialization?

In my spring boot project, I noticed a strange Jackson behavior. I searched over internet, found out what to do, but haven't found out why.
UserDto:
#Setter
#Getter
#AllArgsConstructor
public class UserDto {
private String username;
private String email;
private String password;
private String name;
private String surname;
private UserStatus status;
private byte[] avatar;
private ZonedDateTime created_at;
}
Adding a new user works just fine.
TagDto:
#Setter
#Getter
#AllArgsConstructor
public class TagDto {
private String tag;
}
Trying to add a new tag ends with an error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of TagDto (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
The solution to the problem was to add zero-arg constructor to the TagDto class.
Why does Jackson require no-arg constructor for deserialization in TagDto, while working just fine with UserDto?
Used same method for adding both.
My Tag and User entities are both annotated with
#Entity
#Setter
#Getter
#NoArgsConstructor
and have all args constructors:
#Entity
#Setter
#Getter
#NoArgsConstructor
public class User extends AbstractModel {
private String username;
private String password;
private String email;
private String name;
private String surname;
private UserStatus status;
#Lob
private byte[] avatar;
#Setter(AccessLevel.NONE)
private ZonedDateTime created_at;
public User(final String username, final String password, final String email, final String name, final String surname) {
this.username = username;
this.password = password;
this.email = email;
this.name = name;
this.surname = surname;
this.created_at = ZonedDateTime.now();
}
}
#Entity
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Tag extends AbstractModel {
private String tag;
}
#MappedSuperclass
#Getter
public abstract class AbstractModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
}
Entity generation:
#PostMapping(path = "/add")
public ResponseEntity<String> add(#Valid #RequestBody final D dto) {
this.abstractModelService.add(dto);
return new ResponseEntity<>("Success", HttpStatus.CREATED);
}
public void add(final D dto) {
//CRUD repository save method
this.modelRepositoryInterface.save(this.getModelFromDto(dto));
}
#Override
protected Tag getModelFromDto(final TagDto tagDto) {
return new Tag(tagDto.getTag());
}
#Override
protected User getModelFromDto(final UserDto userDto) {
return new User(userDto.getUsername(), userDto.getPassword(), userDto.getEmail(), userDto.getName(), userDto.getSurname());
}
Error occurs when parsing JSON
{"tag":"example"}
sent via postman localhost:8081/tag/add, returns
{
"timestamp": "2020-09-26T18:50:39.974+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/tag/add"
}
I am using Lombok v1.18.12 and Spring boot 2.3.3.RELEASE with Jackson v2.11.2.
TL;DR: Solution is at the end.
Jackson supports multiple ways of creating POJOs. The following lists the most common ways, but it likely not a complete list:
Create instance using no-arg constructor, then call setter methods to assign property values.
public class Foo {
private int id;
public int getId() { return this.id; }
#JsonProperty
public void setId(int id) { this.id = id; }
}
Specifying #JsonProperty is optional, but can be used to fine-tune the mappings, together with annotations like #JsonIgnore, #JsonAnyGetter, ...
Create instance using constructor with arguments.
public class Foo {
private int id;
#JsonCreator
public Foo(#JsonProperty("id") int id) {
this.id = id;
}
public int getId() {
return this.id;
}
}
Specifying #JsonCreator for the constructor is optional, but I believe it is required if there is more than one constructor. Specifying #JsonProperty for the parameters is optional, but is required for naming the properties if the parameter names are not included in the class file (-parameters compiler option).
The parameters imply that the properties are required. Optional properties can be set using setter methods.
Create instance using factory method.
public class Foo {
private int id;
#JsonCreator
public static Foo create(#JsonProperty("id") int id) {
return new Foo(id);
}
private Foo(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
}
Create instance from text value using String constructor.
public class Foo {
private int id;
#JsonCreator
public Foo(String str) {
this.id = Integer.parseInt(id);
}
public int getId() {
return this.id;
}
#JsonValue
public String asJsonValue() {
return Integer.toString(this.id);
}
}
This is useful when a the POJO has a simply text representation, e.g. a LocalDate is a POJO with 3 properties (year, month, dayOfMonth), but is generally best serialized as a single string (yyyy-MM-dd format). #JsonValue identifies the method to be used during serialization, and #JsonCreator identifies the constructor/factory-method to be used during deserialization.
Note: This can also be used for single-value construction using JSON values other than String, but that is very rare.
Ok, that was the background information. What is happening for the examples in the question, it that UserDto works because there is only one constructor (so #JsonCreator is not needed), and many arguments (so #JsonProperty is not needed).
However, for TagDto there is only a single-argument constructor without any annotations, so Jackson classifies that constructor as a type #4 (from my list above), not a type #2.
Which means that it is expecting the POJO to be a value-class, where the JSON for the enclosing object would be { ..., "tag": "value", ... }, not { ..., "tag": {"tag": "example"}, ... }.
To resolve the issue, you need to tell Jackson that the constructor is a property initializing constructor (#2), not a value-type constructor (#4), by specifying #JsonProperty on the constructor argument.
This means that you cannot have Lombok create the constructor for you:
#Setter
#Getter
public class TagDto {
private String tag;
public TagDto(#JsonProperty("tag") String tag) {
this.tag = tag;
}
}

Jackson: remove some values from json and keep some null values

I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}

Serializing multiple tiers of XML elements

Let's say I'm trying to create an XML document out of an object. Is this possible using JAXB annotations on a single Food class, or do I need to create inner classes for Cost and Flavor?
I know I can use #XmlElement or #XmlAttribute to set up immediate children of my root element. However, I'm not sure if/how to create the <cost> and <Flavor> tags as I show here.
<Food>
<cost amt=13.5 unit=USD/>
<Flavor spicy=5>It tastes good</Flavor>
</Food>
#XmlRootElement("Food")
public class Food {
private float amount;
private String units;
private String flavorType;
private STring flavorDescription;
}
Add a new Java class Cost:
public class Cost
{
#XmlAttribute
double amt;
#XmlAttribute
String unit;
}
And exdend class Food
#XmlRootElement
public class Food {
private float amount;
private String units;
private String flavorType;
private String flavorDescription;
private Cost cost;
...
you could also use something like this for Flavor class
public class Flavor {
private long spicy;
private String shortDesc;
#XmlValue
public String getShortDesc() {
return shortDesc;
}
public void setShortDesc(String shortDesc) {
this.shortDesc = shortDesc;
}
#XmlAttribute
public Long getSpicy() {
return spicy;
}
public void setSpicy(long spicy) {
this.spicy= spicy;
}
}

Gson - Read a value with two different keys

In my Android project I have two types of response where both response are identical except two keys.
Response 1
{"fullName":"William Sherlock Scott Holmes","address":"221B Baker Street, London, England, UK","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
Response 2
{"name":"Sherlock","city":"London","downloads":642,"rating":3,"repos":["https://link1","https://link2","https://link3"]}
If you see the responses only two key names are changing fullName/name and address/city
I don't want to create one more pojo for other response. My question is: is it possible to use only one Pojo to read both responses?
public class AccountInfo {
private String name;
private String city;
//other objects
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
//other setters and getters
}
Any help will be appreciated...
You can annotate the members to accept values from two different json names using the #SerializedName annotation:
#SerializedName(value = "name", alternate = {"fullName"})
private String name;
#SerializedName(value = "city", alternate = {"address"})
private String city;
Either named element can then be placed into the members that are annotated like this.
UPDATED :
#SerializedName alternate names when deserializing is added in Version 2.4
Yes, you can totally use one POJO class for deserializing both responses. Your POJO class will contain keys from both responses.
public class Response {
private String name;
private String city;
private String fullName;
private String address;
private Integer downloads;
private Integer rating;
private List<String> repos ;
}
But when using the Response class, be careful that for first response, the name and city will be null, and for the second one, the address and fullname.
Yeah you can do that in a single POJO. Try this:
public class POJO {
#SerializedName("name")
public String name;
#SerializedName("city")
public String city;
#SerializedName("fullName")
public String fullName;
#SerializedName("address")
public String address;
#SerializedName("downloads")
public Integer downloads;
#SerializedName("rating")
public Integer rating;
#SerializedName("repos")
public List<String> repos = new ArrayList<String>();
}
While parsing you have to check values for null. For eg -
While Parsing Response 1: name and city variables will be null
While Parsing Response 2: fullname and address will be null
Note : Try checking values for null before using else you'll get nullpointerexception
Define all possible fields in your POJO Class like
public class AccountInfo {
private String name;
private String city;
private String fullname;
private String address;
}
While performing operation check for null in those feilds

Categories