I have two entities: Category and Item.
Category Entity:
#JsonInclude(Include.NON_EMPTY)
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
#Entity
public class Category {
#Id
#GeneratedValue
private int id;
private String categoryName;
#OneToMany(mappedBy = "category")
private List<Item> itemList;
//have getters and setters
}
Item Entity:
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
#Entity
public class Item {
#Id
#GeneratedValue
private int id;
private String itemName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_Category_id")
private Category category;
//have getters and setters
}
After doing a left join query like this:
"SELECT c from Category c Left JOIN FETCH c.itemList il"
I got the result and then I have done hibernate aware JSON serialization to the result.
My HibernateAwareObjectMapper is:
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hibernateModule = new Hibernate4Module();
hibernateModule.disable(Hibernate4Module.Feature.FORCE_LAZY_LOADING);
registerModule(hibernateModule);
}
}
And HibernateAwareSerializerFactory is:
public class HibernateAwareSerializerFactory extends BeanSerializerFactory {
protected HibernateAwareSerializerFactory(SerializerFactoryConfig config) {
super(config);
}
}
In my dispatcher servlet I have written:
<mvc:annotation-driven>
<mvc:message-converters>
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.scinv.hibernateAwareMapper.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
So, I have got a JSON Like this:
{
"ArrayList": [
{
"#id": "971ef69e-1605-46f2-8234-b595e38be11a",
"id": 1,
"categoryName": "fruit",
"itemList": [
{
"#id": "75a3a7e5-ce66-4f6d-a04c-d04145d92b21",
"id": 1,
"itemName": "mango",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
},
{
"#id": "0aa0fb71-2909-4909-8403-0765829ee8c1",
"id": 2,
"itemName": "apple",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
},
{
"#id": "02c381cb-33fa-45a6-bff9-ec146357f4bc",
"id": 3,
"itemName": "orange",
"category": "971ef69e-1605-46f2-8234-b595e38be11a"
}
]
},
"971ef69e-1605-46f2-8234-b595e38be11a",
"971ef69e-1605-46f2-8234-b595e38be11a"
]
}
In this JSON,
there is "#id": "971ef69e-1605-46f2-8234-b595e38be11a"
which is Object Id for Category Entity.
And there is "#id": "75a3a7e5-ce66-4f6d-a04c-d04145d92b21"
which is Object Id for Item Entity.
Category has 3 items So Catagory's object Id is also showing 2 more times.
Like this: "971ef69e-1605-46f2-8234-b595e38be11a","971ef69e-1605-46f2-8234-b595e38be11a"
That Generated JSON serialization is as expected.
But I just need to hide that Object reference "#id" in that json. This "#id" is by default created by #JsonIdentityInfo.
So I need configuration for #JsonIdentityInfo to hide Object Id "#id".
I have tried #JsonIgnore but failed.
I have googled 2 days for solution but failed.Can anyone give me a solution or a suggestion?
Related
I'm building a REST API with Spring Boot to retrieve boat information. I'm using Spring Data Rest and Spring Data JPA. When I get the data from the API, I don't know why the relationship data are not with the others informations.
Do I have to configure something in Spring to get the relationship with my data ?
Here is my file.
Boat entity:
#Entity
#Table(name="boat")
#Data
public class Boat {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "type_id", nullable = false)
#JsonBackReference
private BoatType type;
}
Boat type entity :
#Entity
#Table(name = "boat_type")
#Data
public class BoatType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
#JsonManagedReference
private Set<Boat> boats;
}
Boat repository :
#CrossOrigin("http://localhost:4200")
public interface BoatRepository extends JpaRepository<Boat, Long> {
}
JSON response :
{
"_embedded": {
"boats": [
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"_links": {
"self": {
"href": "http://localhost:8080/api/boats/1"
},
"boat": {
"href": "http://localhost:8080/api/boats/1"
},
"type": {
"href": "http://localhost:8080/api/boats/1/type"
}
}
},
...
]
}
Result expected (with the type object too) :
{
"_embedded": {
"boats": [
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"type": {
"id": 1,
"name": "Motorboats"
},
"_links": {
"self": {
"href": "http://localhost:8080/api/boats/1"
},
"boat": {
"href": "http://localhost:8080/api/boats/1"
},
"type": {
"href": "http://localhost:8080/api/boats/1/type"
}
}
},
...
]
}
I think that the problem is related with Spring Data Rest because when i do the same app with my own controller and repository, i get the data I need.
Is there a way to "configure" spring data rest?
It seems like you've used #JsonBackReference and #JsonManagedReference the other way around, than you needed. You've put #JsonBackReference on the type field in your Boat class, whereas its documentation states:
[...] Linkage is handled such that the property annotated with this annotation is not serialized
So it seems like you need to put #JsonManagedReference annotation on it instead (see: JsonManagedReference documentation) and put #JsonBackReference on boats in your BoatType class.
Alternatively, you could consider using #JsonIdentityInfo instead. See: the documentation.
Also, this article might be helpful. It explains various ways to handle bidirectional relationships using Jackson.
Change #JsonManagedReference and #JsonBackReference to #JsonIgnoreProperties.
In your case:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "type")
#JsonIgnoreProperties(value = {"type"})
private Set<Boat> boats;
and
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "type_id", nullable = false)
#JsonIgnoreProperties(value = {"boats"})
private BoatType type;
You will avoid the infinity loop in json result and get all reference objects (relationships).
The Boat response includes a uri to your BoatType resource by default since you defined a rest repository for your BoatType resource (docs)
To override this behaviour, define a projection to expose the boat type data (docs):
#Projection(name = "boatDetail", types = { Boat.class })
interface BoatDetail {
// ... all other fields you want included in the response
BoatType getType();
}
Then include the projection as a query parameter:
{apiUrl}boats/1?projection=boatDetail
The response should now include the boat type data:
{
"id": 1,
"name": "Boat 1",
"description": "A brief description of the boat 1",
"type": {
"id": 1,
"name": "Motorboats"
},
"_links": {
// ...
}
}
To automatically include the projection on a collection of resources, use an excerpt (docs):
#RepositoryRestResource(excerptProjection = BoatDetail.class)
interface BoatRepository extends JpaRepository<Boat, Long> {}
Then the http response:
{
"_embedded":{
"boats":[
{
"id":1,
"name":"Boat 1",
"description":"A brief description of the boat 1",
"type":{
"id":1,
"name":"Motorboats"
},
"_links":{
//...
}
},
// ...
]
}
}
I have a Film model like below:
public class Film{
private String name;
private String category;
private Actor[] actors;
private MarketingPlan marketingPlan;
}
class Actor{
private String name;
private double salary;
}
class MarketingPlan{
private String nameOfPlan;
private Country[] affectedCountries;
}
class Country{
private int countryID;
private String marketingText;
}
which generate a JSON like this:
{
"name": "My First Film",
"category": "action",
"marketingPlan": {
"name": "plan1",
"affectedCountries": [
{
"marketingText": "This is a marketing text",
"countryID": 332
}
]
},
"actors": [
{
"name": "John",
"salary": 123456
}
]
}
I would like to represent my JSON result above as tree structure in JavaFX. Can anyone give me some hint or suggest me some tutorial how can I do it. Thank you very much in advanced!
Course.java
#Entity
#Table
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Course {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotEmpty
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "subject_course", joinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "course_id", referencedColumnName = "id"))
private Set<Subject> subjects = new HashSet<Subject>();
---- getter/setter ----
Subject.java
#Entity
#Table
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Subject {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String description;
#ManyToMany(mappedBy = "subjects", fetch=FetchType.EAGER)
#Cascade({CascadeType.DELETE, CascadeType.SAVE_UPDATE})
private Set<Course> courses = new HashSet<Course>();
---- getter/setter ----
Request configuration in Spring:
#RequestMapping(value = "/courses", method = RequestMethod.GET)
public #ResponseBody ResponseEntity<?> getAllCourses() {
List<Course> courses = courseService.getAllCourses();
if (courses.isEmpty()) {
return new ResponseEntity<Message>(new Message("error", "No course found!"), HttpStatus.NOT_FOUND);
}
return new ResponseEntity<List<Course>>(courses, HttpStatus.OK);
}
Hibernate Version: 4.2.0.Final
Spring Version: 3.2.8.RELEASE
Jackson:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.4</version>
</dependency>
Expecting O/P
[{
"id": 1,
"description": "BCA",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3] //Either show blank array or only ids
}]
},{
"id": 2,
"description": "BSC",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3]
}]
},{
"id": 3,
"description": "BA",
"subjects":[{
"id":1,
"description":"Physics",
"courses":[1,2,3]
}]
},]
But getting O/P:
[
{
"id": 1,
"description": "BCA",
"subjects": [
{
"id": 1,
"description": "Math",
"staffs": [],
"courses": [
{
"id": 4,
"description": "BDA",
"subjects": [
1
],
"students": []
},
{
"id": 3,
"description": "BBA",
"subjects": [
1
],
"students": []
},
1
],
"students": []
}
],
"students": [
{
"id": 1,
"name": "",
"age": 0,
"gender": null,
"course": 1,
"subjects": []
}
]
},
3,
4
]
As per actual o/p, it is stopping the recursion at second level. But my requirement is not to repeat the same objects data from child... It means Course must not repeat its data in Subject's course property. Similarly, if call the same from Subject then subject should not repeat Course subject property value. It is better to skip, if can't then just display id values separated by comma.
Please advise how to fix this issue.
You can use #JsonIgnore in the Subject class like this:
#ManyToMany(mappedBy = "subjects", fetch=FetchType.EAGER)
#Cascade({CascadeType.DELETE, CascadeType.SAVE_UPDATE})
#JsonIgnore
private Set<Course> courses = new HashSet<Course>();
I am consuming some XML exposed from a REST application and want to expose this as JSON in my own REST service.
Right now I have the following POJO:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"salesMarket"
})
#XmlRootElement(name = "salesMarkets")
public class SalesMarkets {
protected List<SalesMarket> salesMarket;
public List<SalesMarket> getSalesMarket() {
if (salesMarket == null) {
salesMarket = new ArrayList<SalesMarket>();
}
return this.salesMarket;
}
}
Which produces the following JSON:
"salesMarkets": {
"salesMarket": [
{
"brands": {
"brand": [
"DAN"
]
},
"code": "999"
},
{
"brands": {
"brand": [
"DAN"
]
},
"code": "208"
}
]
}
My question is (using Jackson annnotations), is there a way to avoid the class name being serialized as JSON??, so I instead would have:
"salesMarket": [{
"brands": {
"brand": [
"DAN"
]
},
"code": "999"
}, {
"brands": {
"brand": [
"DAN"
]
},
"code": "208"
}]
I'm thinking some Jackson annotaion on the class SalesMarkets... But havn't been succesfull yet :-(
UPDATE:
Just realised that the SalesMarket class was referenced from another class - which is why the "salesMarkets" appears in the JSON. Is there a way to annotate the SalesMarkets field, so that it is ignored but not the fields contained there in?:
#XmlRootElement(name = "product")
public class Product {
#XmlElement(required = true)
protected String propertyID;
#XmlElement(required = true)
protected String season;
**protected SalesMarkets salesMarkets;**
protected Information information;
protected Features features;
protected Location location;
protected Address address;
protected Buildings buildings;
protected Pictures pictures;
protected Media media;
protected Prices prices;
protected Offers offers;
protected Availabilities availabilities;
protected Services services;
protected Concepts concepts;
...
You need to either remove
#XmlRootElement(name = "salesMarkets")
Or disable the feature on ObjectMapper:
objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true)
To further unwrap salesMarkets field in Product instances you can do the following:
public class Product {
protected SalesMarkets salesMarkets;
public List<SalesMarket> getSalesMarkets(){
if(salesMarkets != null){
return salesMarkets.getSalesMarket();
} else {
return null;
}
}
}
colleagues!
We want to write Rest Client to service which follow the HATEOAS principle. So we have the following HAL+JSON representation and we want to deserialize it using spring-hateoas :
{
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"},
"artist": {"href": "http://localhost:8080/rest/artist/1"}
},
"_embedded": {
"albums": [{ //can be array or object
"id": "1",
"title": "album title",
"artistId": "1",
"stockLevel": 2,
"_links": {
"self": {"href": "http://localhost:8080/rest/albums/1"}
}
}],
"artist": { //can be array or object
"id": "1",
"name": "artist name",
"_links": {
"self": {"href": "http://localhost:8080/rest/artist/1"}
}
} //....
}
}
We expected the java object like this:
HalResource {
Resource<Album> //entity
List<Link> // _links
List<Resource<BaseEntity>>{ //_embedded
Resource<Album>
Resource<Artist>
....
}
}
So we have custom resource representation with embedded(list of resources) and entity(single resource):
#XmlRootElement(name = "resource")
public class HalResource<EntityType, EmbeddedType> extends Resources<EmbeddedType> {
#JsonUnwrapped
private EntityType entity;
public HalResource() {
}
public HalResource(Iterable<EmbeddedType> content, Link... links) {
super(content, links);
}
public EntityType getEntity() {
return entity;
}
public void setEntity(EntityType entity) {
this.entity = entity;
}
}
DTO classes:
public abstract class BaseEntity{}
#XmlRootElement(name = "album")
public class Album extends BaseEntity {
private String id;
private String title;
private String artistId;
private int stockLevel;
// getters and setters...
}
#XmlRootElement(name = "artist")
public class Artist extends BaseEntity {
private String id;
private String name;
// getters and setters...
}
And we want to get something like this, where Entity will be Artist or Album, but HalResourcesDeserializer return Resource.class with null content.
HalResource<Album, Resource<Entity>> resources =
restClient.getRootTarget().path("albums/1").queryParam("embedded", true).request().accept("application/hal+json")
.get(new GenericType<HalResource<Album, Resource<Entity>>>() {});
By using #JsonTypeInfo and #JsonSubTypes anotations we successfully deserialized our JSON(you can see the example on the github), but we don't want to have some additional type filds and anotattions in our DTO and JSON format.
We see one solution that is create a custom deserializer which can processing that.
So the question is: What is the convenient way to deserialize our JSON(links + embedded container) using spring-hateoas?
We use spring-hateoas 0.16v(but we tried 0.19v) and glassfish jersey 2.22.1
Thank you!