To explain my issue, let's say that I'm retrieving the following OData V2 Entity:
{
"d": {
"EmployeeID": 1,
"LastName": "Davolio",
"FirstName": "Nancy",
"Orders": {
"results": [
{
"OrderID": 10258
}
]
},
"Territories": {
"results": [
{
"TerritoryID": "06897"
}
]
}
}
}
And I have the corresponding model Class:
#JsonRootName(value = "d")
public class Employee {
#JsonProperty("EmployeeID")
private int employeeId;
#JsonProperty("LastName")
private String lastName;
#JsonProperty("FirstName")
private String firstName;
#JsonProperty("Orders")
private List<Order> orders;
#JsonProperty("Territories")
private List<Territory> territories;
...
}
As expected the exception com.fasterxml.jackson.databind.exc.MismatchedInputException is being triggered because the "Orders" and "Territories" collections are actually within the property "results", as the OData V2 states.
Do you guys have any idea how to ignore the nested "results" property and get the lists straight away?
Is a custom deserializer or wrapper class really needed in this case?
Thanks!
There are always multiple ways to tackle this problem.
One is to create a wrapper class and have Employee Directly reference it.
For example:
public class WrapperDto<T> implements Serializable {
#JsonProperty("results")
private List<T> elements;
}
public class Employee {
...
#JsonProperty("Orders")
private WrapperDto<Order> orders;
#JsonProperty("Territories")
private WrapperDto<Territory> territories;
...
}
when you want to get the orders, you have to call orders.getResults() to get the List
another solution can be found here where you have a custom wrapper:
How to map a nested value to a property using Jackson annotations?
Related
In response to api call, i'm sending Json Class Object as response.
I need response like this without empty objects being removed.
{
"links": {
"products": [],
"packages": []
},
"embedded":{
"products": [],
"packages": []
}
}
but final Response is looking like this
{
"links": {},
"embedded": {}
}
Two things to be aware of:
null and empty are different things.
AFAIK Jackson is configured to serialize properties with null values by default.
Make sure to properly initialize your properties in your object. For example:
class Dto {
private Link link;
private Embedded embedded;
//constructor, getters and setters...
}
class Link {
//by default these will be empty instead of null
private List<Product> products = new ArrayList<>();
private List<Package> packages = new ArrayList<>();
//constructor, getters and setters...
}
Make sure your classes are not extending another class with this annotation #JsonInclude(JsonInclude.Include.NON_NULL). Example:
//It tells Jackson to exclude any property with null values from being serialized
#JsonInclude(JsonInclude.Include.NON_NULL)
class BaseClass {
}
//Any property with null value will follow the rules stated in BaseClass
class Dto extends BaseClass {
private Link link;
private Embedded embedded;
//constructor, getters and setters...
}
class Link extends BaseClass {
/* rest of the design */
}
If you have the latter and you cannot edit BaseClass then you can define different rules in the specific classes:
class Link extends BaseClass{
//no matter what rules are defined elsewhere, this field will be serialized
#JsonInclude(JsonInclude.Include.ALWAYS)
private List<Product> products;
//same here
#JsonInclude(JsonInclude.Include.ALWAYS)
private List<Package> packages;
//constructor, getters and setters...
}
I have a json like -
{
"type" : "employee",
"details" : {
"name" : "ABC",
"age" : 12,
"sex" : "male"
}
}
And a Java Class like -
public class Person {
String name;
String sex;
String type;
int age;
----getters and setters
}
I was wondering is there a ways to directly map the attributes of the details object to the person class like details.name to Person.name.
I know this can be achieved with custom deserializers, but I was hoping to avoid it. May be some annotations that GSON or Jackson provides.
There are a few ways to solve this, but what I would do is create the following class:
public class PersonWrapper {
private String type;
#JsonProperty("details")
private Person person;
}
EDIT:
If you don't want to add a wrapper class, you can try adding #JsonRootName(value = "details") to your Person class.
you can use #JsonProperties for mapping
When I execute request to db
db.users.find({"name": "Max"})
I get this result
{"_id":ObjectId("5785718ee271a7c7ebaad28b"),"name":"Max","visits-by-day":[{"day":"Thursday","visitsAmount":20},{"day":"Saturday","visitsAmount":4}]}
JSON structure example:
{
"users": [
{
"name": "Bobby",
"visits-by-day": [
{
"day": "Sunday",
"visitsAmount": 8
},
{
"day": "Monday",
"visitsAmount": 3
}
]
}
]
}
Here my Java code
MongoUser user = mongoTemplate.findOne(query(where("name").is("Max")), MongoUser.class);
The model
#Document
public class MongoUser {
#Id
private String id;
private String name;
private List<VisitsPerDay> visitsByDay;
// Getters & Setters omitted
}
public class VisitsPerDay {
private String day;
private Integer visitsAmount;
// Getters & Setters omitted
}
Why Spring does return a null empty instead of serialized Java object?
By default, the collection queried for a given typed is derived from the simple name of the domain type you want to read. In your case, that would be mongoUser. To get your example to work, you basically have two options:
Explicitly configure the collectionName in the #Document annotation on MongoUser to users. That will basically tie instances of that class to that collection and let all data access operations for that class to work with that collection (e.g. for repositories etc.).
When calling MongoTemplate, use the overload of findOne(…) that takes an explicit collection name:
template.findOne(query(…), MongoUser.class, "users");
I have a class which looks like this :
public class Item {
private ItemHeader header;
private ItemBody body;
}
public class ItemHeader {
private int id;
private String name;
}
public class ItemBody {
private List<String> values;
private List<String> details;
}
The fields of ItemHeader and ItemBody are accessible via setters and getters from the Item class as well as from their corresponding classes. All said setters and getters EXCEPT get/setItemBody +get/setItemHeader are annotated with #JsonIgnore.
When Item instance is returned by a GET REST method, the Response looks as following :
{
"body": {
"details":[]
"values":[]
},
"header": {
"id": 145,
"name": "name_of_item",
},
"details":[],
"values":[],
"id": 145,
"name": "name_of_item"
}
Internals of itemHeader and itemBody are spilled twice into the deserialized Json , once (correctly) inside the corresponding fields and the second time just outside them.
I do not have much control over Jackson definitions behind the scenes and can basically only control my class hierarchy with annotations and such.
Please advise - how to remove the duplication, the "spilled over" values...
I've ended up removing the double layer of getters/setters which solved the issue, and in the process discovered that indeed, the serialization path and the de- path used different libraries . So https://stackoverflow.com/users/2513573/adamskywalker had the right idea
Lets assume I have the following entities:
#Entity
public class Registration {
#ManyToOne
private Student student;
//getters, setters
}
#Entity
public class Student {
private String id;
private String userName;
private String name;
private String surname;
//getters, setters
}
#Projection(name="minimal", types = {Registration.class, Student.class})
public interface RegistrationProjection {
String getUserName();
Student getStudent();
}
I'm trying to create the following JSON representation, So when I use http://localhost:8080/api/registrations?projection=minimal I dont need all the user data to come along:
{
"_links": {
"self": {
"href": "http://localhost:8080/api/registrations{?page,size,sort,projection}",
}
},
"_embedded": {
"registrations": [
{
"student": {
"userName": "user1"
}
}
],
"_links": {
"self": {
"href": "http://localhost:8080/api/registrations/1{?projection}",
}
}
}
}
However the Projection I have created I get an exception(it works without the getUserName() statement. Obviously I have defined the interface in the wrong way...but which is the correct way to do something like this?
EDIT: Exception is the following
Invalid property 'userName' of bean class [com.test.Registration]:
Could not find field for property during fallback access! (through reference chain: org.springframework.hateoas.PagedResources["_embedded"]
->java.util.UnmodifiableMap["registrations"]->java.util.ArrayList[0]->org.springframework.data.rest.webmvc.json.["content"]
->$Proxy119["userName"])</div></body></html>
As exception said userName is not a member of Registration entity. That why it failed. But I think you already understand that.
First at all if you just want to expose its projection for Registration you should first change the following line:
#Projection(name="minimal", types = {Registration.class, Student.class})
By setting types = {Registration.class, Student.class} you asked Spring Data Rest to apply projection on Registration.class and Student.class. And that can cause some issue because depending of the type you should not have the same member/methods. In practice types must share a common ancestor.
Otherwise for the main problem you should try virtual projections the following thing (I didn't try on your sample but it should work, I hope):
#Projection(name="minimal", types = {Registration.class})
public interface RegistrationProjection {
#Value("#{target.getStudent().getUserName()}")
String getUserName();
}
I don't know if you are familiar with SPeL but the previous code just create a getUserName like this.getStudent().getUserName() because target is bound on the instance object (see documentation).