I have a HATEOAS Spring rest API that connects to a mysql database. I have no control over the database schema and it changes periodically so I am periodically generating the entity classes and updating the service.
I have three files per route. A generated entity class, a controller and a repository file that uses the PagingAndSortingRepository class to automatically serve my entities without much configuration.
Entity class
package hello.models;
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name = "animals", schema = "xyz123", catalog = "")
public class AnimalsEntity {
private Integer id;
private String name;
private String description;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true, length = 80)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "description", nullable = true, length = 255)
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleEntity that = (RoleEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(description, that.description);
}
#Override
public int hashCode() {
return Objects.hash(id, name, description);
}
}
Repository class
package hello.repositories;
#RepositoryRestResource(collectionResourceRel = "animals", path = "animals")
public interface AnimalsRepository extends PagingAndSortingRepository<AnimalEntity, String> {
// Allows /animal/cheetah for example.
AnimalEntity findByName(String name);
// Prevents POST /element and PATCH /element/:id
#Override
#RestResource(exported = false)
public AnimalEntity save(AnimalEntity s);
// Prevents DELETE /element/:id
#Override
#RestResource(exported = false)
public void delete(AnimalEntity t);
}
Controller class
package hello.controllers;
import hello.models.AnimalsEntity;
import hello.repositories.AnimalsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
#RepositoryRestController
#RequestMapping("/animals")
class PrinterController {
#Autowired
private AnimalsRepository animalsRepo;
#RequestMapping("/{name}")
public #ResponseBody
List<AnimalsEntity> findAnimal(#PathVariable(value = "name") String name) {
return animalsRepo.findByName(name);
}
}
I want my HATEOAS API to serve things with the pagination/sorting options. They currently serves responses like..
{
"links" : [ {
"rel" : "first",
"href" : "http://localhost:8080/animals?page=0&size=20",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
}, {
"rel" : "self",
"href" : "http://localhost:8080/animals{?page,size,sort}",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
}, {
"rel" : "next",
"href" : "http://localhost:8080/animals?page=1&size=20",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
}, {
"rel" : "last",
"href" : "http://localhost:8080/animals?page=252&size=20",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
}, {
"rel" : "profile",
"href" : "http://localhost:8080/profile/animals",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
}, {
"rel" : "search",
"href" : "http://localhost:8080/animals/search",
"hreflang" : null,
"media" : null,
"title" : null,
"type" : null,
"deprecation" : null
} ],
"content" : [ {
"id" : 1,
"name" : "cheetah",
"description": "xyz
]
},{
"id" : 2,
"name" : "tortise",
"description": "xyz
]
}],
"page" : {
"size" : 20,
"totalElements" : 5049,
"totalPages" : 253,
"number" : 0
}
}
This is awesome. But I need my javascript applications to access the rest api like (GET) /animals/cheetah.
Normally I would change the schema and set #Id on the name property in the entity class but I cannot do that in this instance. I cant change the database schema and eventually I want to dynamically generate these entity classes to allow for easy schema changes.
I've figured out that I can override the endpoint and serve it manually but I lose the pagination/HATEOAS formatting.
[
{
"id": 1,
"name": "cheetah",
"description": "xyz"
},
{
"id": 2,
"name": "tortise",
"description": "xyz"
}
]
How do I accomplish the #Id change without losing the JSON format or changing the entity class?
I found my answer here: https://docs.spring.io/spring-data/rest/docs/current/reference/html/#_customizing_item_resource_uris
You can map other fields as the default lookup.
Related
I want to serialize a data structure to JSON with Jackson 2.13.3. The serialization works, only it does not work in the way I intend.
I created a simplified example data structure to show what the desired serialization is.
The data structure consists of a main Container, which contains a list of Elements. The elements have some links between them.
In the example I create the following linking structure of the elements:
startTop <--> endTop
^ ^
| |
| |
v v
startBottom <--> endBottom
I want to get the following output
The goal is that the serialization represents the linkage data via the IDs of the linked information. The full serialization of the elements should only occur in the top level list of the container. This does not correspond the order in which jackson encounters the elements during serialization.
{
"allElements": [{
"id": "startBottom",
"successor": "endBottom",
"predecessor": null,
"upperNeighbours": ["startTop", "endTop"],
"lowerNeighbours": null
},
{
"id": "endBottom",
"successor": null,
"predecessor": "startBottom",
"upperNeighbours": null,
"lowerNeighbours": null
},
{
"id": "startTop",
"successor": "endTop",
"predecessor": null,
"upperNeighbours": null,
"lowerNeighbours": ["startBottom"]
},
{
"id": "endTop",
"successor": null,
"predecessor": "startTop",
"upperNeighbours": null,
"lowerNeighbours": ["startBottom"]
}
]
}
I do get the following output
jackson puts the full serialization of an object wherever it encounters the object first, as can be seen in the output I currently get.
{
"allElements" : [ {
"id" : "startBottom",
"successor" : {
"id" : "endBottom",
"successor" : null,
"predecessor" : "startBottom",
"upperNeighbours" : null,
"lowerNeighbours" : null
},
"predecessor" : null,
"upperNeighbours" : [ {
"id" : "startTop",
"successor" : {
"id" : "endTop",
"successor" : null,
"predecessor" : "startTop",
"upperNeighbours" : null,
"lowerNeighbours" : [ "startBottom" ]
},
"predecessor" : null,
"upperNeighbours" : null,
"lowerNeighbours" : [ "startBottom" ]
}, "endTop" ],
"lowerNeighbours" : null
}, "endBottom", "startTop", "endTop" ]
}
Process finished with exit code 0
The java code:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import java.util.List;
public class Test {
public static void main(String[] args) throws JsonProcessingException {
Element startBottom = new Element("startBottom");
Element endBottom = new Element("endBottom");
Element startTop = new Element("startTop");
Element endTop = new Element("endTop");
startBottom.setSuccessor(endBottom);
startTop.setSuccessor(endTop);
endBottom.setPredecessor(startBottom);
endTop.setPredecessor(startTop);
startBottom.setUpperNeighbours(List.of(startTop, endTop));
startTop.setLowerNeighbours(List.of(startBottom));
endTop.setLowerNeighbours(List.of(startBottom));
Container container = new Container();
container.setAllElements(List.of(startBottom, endBottom, startTop, endTop));
ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ObjectWriter prettyPrintWriter = mapper.writerWithDefaultPrettyPrinter();
System.out.println(prettyPrintWriter.writeValueAsString(container));
}
}
class Container {
public List<Element> getAllElements() {return allElements;}
public void setAllElements(List<Element> allElements) {this.allElements = allElements;}
private List<Element> allElements;
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
class Element {
Element(String id) {this.id = id;}
private String id;
// May be null
private Element successor;
// May be null
private Element predecessor;
// May be empty, which for us is the same as being null
private List<Element> upperNeighbours;
// May be empty, which for us is the same as being null
private List<Element> lowerNeighbours;
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public Element getSuccessor() {return successor;}
public void setSuccessor(Element successor) {this.successor = successor;}
public Element getPredecessor() {return predecessor;}
public void setPredecessor(Element predecessor) {this.predecessor = predecessor;}
public List<Element> getUpperNeighbours() {return upperNeighbours;}
public void setUpperNeighbours(List<Element> upperNeighbours) {this.upperNeighbours = upperNeighbours;}
public List<Element> getLowerNeighbours() {return lowerNeighbours;}
public void setLowerNeighbours(List<Element> lowerNeighbours) {this.lowerNeighbours = lowerNeighbours;}
}
Edit: Added that the serialization does work, but not in the intended way.
Just mark your members successor and predecessor in your class Element with annotation #JsonIgnore to prevent endless loop. And your code should work. But I can even suggest something that will make it even simpler. I wrote my own utility that allows you to serialize any class to Json and deserialize from Json back into an instance of a class. It uses Jackson library under the hood. The code that would do the job for you would be as simple as this:
try {
System.out.println(JsonUtils.writeObjectToJsonString(container));
}catch(IOException ioe) {
//Handle exception
}
Where container is the instance of your container class. Here is the Javadoc for class JsonUtils. This class is part of MgntUtils library written and maintained by me. The library can be obtained as Maven artifacts or on Github (including source code and Javadoc)
As #Thomas commented, I have to add before the fields in question:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
The element class then becomes:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
class Element {
Element(String id) {this.id = id;}
private String id;
// May be null
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private Element successor;
// May be null
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private Element predecessor;
// May be empty, which for us is the same as being null
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private List<Element> upperNeighbours;
// May be empty, which for us is the same as being null
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private List<Element> lowerNeighbours;
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public Element getSuccessor() {return successor;}
public void setSuccessor(Element successor) {this.successor = successor;}
public Element getPredecessor() {return predecessor;}
public void setPredecessor(Element predecessor) {this.predecessor = predecessor;}
public List<Element> getUpperNeighbours() {return upperNeighbours;}
public void setUpperNeighbours(List<Element> upperNeighbours) {this.upperNeighbours = upperNeighbours;}
public List<Element> getLowerNeighbours() {return lowerNeighbours;}
public void setLowerNeighbours(List<Element> lowerNeighbours) {this.lowerNeighbours = lowerNeighbours;}
}
This (with the other code from the question) generates the desired output:
{
"allElements" : [ {
"id" : "startBottom",
"successor" : "endBottom",
"predecessor" : null,
"upperNeighbours" : [ "startTop", "endTop" ],
"lowerNeighbours" : null
}, {
"id" : "endBottom",
"successor" : null,
"predecessor" : "startBottom",
"upperNeighbours" : null,
"lowerNeighbours" : null
}, {
"id" : "startTop",
"successor" : "endTop",
"predecessor" : null,
"upperNeighbours" : null,
"lowerNeighbours" : [ "startBottom" ]
}, {
"id" : "endTop",
"successor" : null,
"predecessor" : "startTop",
"upperNeighbours" : null,
"lowerNeighbours" : [ "startBottom" ]
} ]
}
Here i'm using MongoRepository and i need to query a list of objects that includes certain id in an array of objects inside.
The document structure :
{
"_id" : ObjectId("5ccc1c54a3d5eed9a6b8015a"),
"email" : "sineth3#gmail.com",
"name" : "edward3",
"businessName" : "aroma3",
"phone" : "07177222233",
"address" : "no 100 NY",
"bookletSignups" : [
{
"bookletId" : "sample-booklet",
"contactName" : "john doe"
},
{
"bookletId" : "sample-booklet1",
"contactName" : "john doe1"
}
],
"eventSignups" : [
{
"eventId" : "sample-event",
"contactName" : "john doe2"
},
{
"eventId" : "sample-event 1",
"contactName" : "john doe3"
}
],
"infoSignups" : [
{
"infoRequestId" : "sample-info ",
"contactName" : "john doe4"
},
{
"infoRequestId" : "sample-event 1",
"contactName" : "john doe5"
}
],
"webinarSignups" : [
{
"webinarId" : "sample-webinar ",
"contactName" : "john doe6"
},
{
"webinarId" : "sample-webinar 1",
"contactName" : "john doe7"
}
],
"updatedTime" : ISODate("2016-03-03T08:00:00Z")
}
The Repository :
#Repository
public interface UserRepository extends MongoRepository<User,String> {
#org.springframework.data.mongodb.repository.Query(value = "{ 'bookletSignups': { $elemMatch: { 'bookletSignups.bookletId' : ?0 } }}")
List<User> findByBookletId(String id);
}
User model class:
#Id
private String id;
private String email;
private String name;
private String businessName;
private String phone;
private String address;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date createdTime;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date updatedTime;
#Field("bookletSignups")
#DBRef
private List<BookletSignUp> bookletSignups;
#Field("eventSignups")
#DBRef
private List<EventSignUp> eventSignups;
#Field("infoSignups")
#DBRef
private List<InfoSignUp> infoSignups;
#Field("webinarSignups")
#DBRef
private List<WebinarSignUp> webinarSignups;
So im trying to retrieve User objects that includes a bookletSignups object with the passing bookletId value. But the result is empty. What has gone wrong here?
I would say you need to modify your query to looks like this:
#Repository
public interface UserRepository extends MongoRepository<User,String> {
#org.springframework.data.mongodb.repository.Query(value = "{ 'bookletSignups': { $elemMatch: { 'bookletId' : ?0 } }}")
List<User> findByBookletId(String id);
}
If you check MongoDB documentation for $elemMatch, link to documentation, you can see that basically in $elemMatch operator you are using field in embedded object, so you don't need to specify again name of array in which you are searching for objects.
I'm using Spring Boot 2.x, Spring Data REST, Spring HATEOAS.
I've a simple bean:
#Entity
public class Printer extends AbstractEntity {
#NotBlank
#Column(nullable = false)
private String name;
// Ip address or hostname
#NotBlank
#Column(nullable = false)
private String remoteAddress;
#NotNull
#Enumerated(EnumType.STRING)
#Column(nullable = false)
#JsonSerialize(using = PrinterModelSerializer.class)
private PrinterModel model;
#NotNull
#Column(nullable = false, columnDefinition = "BIT DEFAULT 0")
private boolean ssl = false;
// The store where the device is connected
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private Store store;
I added a custom Serializer for the enum PrinterModel:
public class PrinterModelSerializer extends JsonSerializer<PrinterModel> {
#Override
public void serialize(PrinterModel value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject(); // {
gen.writeStringField("key", value.toString());
gen.writeStringField("name", value.getName());
gen.writeBooleanField("fiscal", value.isFiscal());
gen.writeStringField("imageUrl", value.getImageUrl());
gen.writeEndObject(); // }
gen.close();
}
}
When I get the resource printer using Spring Data REST repositories I've:
{
"sid" : "",
"createdBy" : "admin",
"createdDate" : "2018-10-16T12:29:24Z",
"lastModifiedDate" : "2018-10-16T14:12:08.671566Z",
"lastModifiedBy" : "ab48d95f-09f3-40ba-b8ba-e6fd206a2fe6",
"createdByName" : null,
"lastModifiedByName" : null,
"name" : "m30",
"remoteAddress" : "111.222.333.456",
"model" : {
"key" : "EPSON_M30",
"name" : "Epson TM-m30",
"fiscal" : false,
"imageUrl" : "https://www.epson.it/files/assets/converted/550m-550m/0/0/1/d/001d0815_pictures_hires_en_int_tm-m30_w_frontpaperloading_paper.tif.jpg"
}
}
As you can see I don't have HATEOAS links. If I remove my custom serializer instead I've the right reply:
{
"sid" : "",
"createdBy" : "admin",
"createdDate" : "2018-10-16T12:29:24Z",
"lastModifiedDate" : "2018-10-16T14:12:08.671566Z",
"lastModifiedBy" : "ab48d95f-09f3-40ba-b8ba-e6fd206a2fe6",
"createdByName" : null,
"lastModifiedByName" : null,
"name" : "m30",
"remoteAddress" : "111.222.333.456",
"model" : "EPSON_M30",
"ssl" : true,
"_links" : {
"self" : {
"href" : "http://x.x.x.x:8082/api/v1/printers/3"
},
"printer" : {
"href" : "http://x.x.x.x:8082/api/v1/printers/3{?projection}",
"templated" : true
},
"store" : {
"href" : "http://x.x.x.x:8082/api/v1/printers/3/store{?projection}",
"templated" : true
}
}
}
How can I prevent #JsonSerialize to break HATEOAS links?
Self-referencing JSON with Spring Data REST
In a Spring Data REST data model with Lombok, how can we export and import data to self-referencing JSON without creating a parallel entities or DTOs?
For example, a self-referencing JSON using JSONPath for a simple music manager might look like:
{ "id" : 1,
"albums" : [ {
"id" : 1,
"title" : "Kind Of Blue",
"artist" : "$..artists[?(#.id=1)]",
"tracks" : [ {
"id" : 1,
"title" : "So What",
"duration" : "PT9M5S",
"musicians" : [ {
"musician" : "$..artists[?(#.id=1)]",
}, {
"musician" : "$..artists[?(#.id=2)]",
} ]
}, {
"id" : 3,
"title" : "Blue in Green",
"duration" : "PT5M29S",
"musicians" : [ {
"musician" : "$..artists[?(#.id=1)]",
}, {
"musician" : "$..artists[?(#.id=2)]",
} ]
} ]
} ],
"artists" : [ {
"id" : 1,
"firstName" : "Miles",
"lastName" : "Davis",
"birthDate" : "1926-05-26"
}, {
"id" : 2,
"firstName" : "Bill",
"lastName" : "Evans",
"birthDate" : "1929-09-16"
} ]
}
How can we create the import and export functionality for this representation while retaining the Spring Data REST HATEOAS functionality? The musicians container in an export/import has an array of string JSONPath expressions and in the REST APIs the musicians is an array of Person objects (see below) -- so how can Jackson be configured to select the correct serializer and deserializer the export and import operations?
Details: music manager example
Here's a Spring Boot 1.5, Spring Data Rest, Lombok, and Jackson implementation (GitHub).
Music
#Data
#NoArgsConstructor
#Entity
public class Music {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
private List<Album> albums;
#OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
private List<Person> artists;
}
Album
#Data
#NoArgsConstructor
#Entity
public class Album {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
#ManyToOne(cascade = { CascadeType.ALL })
private Person artist;
#OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
private List<Track> tracks;
}
Track
#Data
#NoArgsConstructor
#Entity
public class Track {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
#JsonSerialize(using = MyDurationSerializer.class)
private Duration duration;
#ManyToMany(cascade = { CascadeType.ALL })
private List<Person> musicians;
public Track(String title, String duration, List<Person> musicians) {
this.title = title;
this.duration = Duration.parse(duration);
this.musicians = musicians;
}
}
Person
#Data
#NoArgsConstructor
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
#JsonSerialize(using = MyLocalDateSerializer.class)
private LocalDate birthDate;
public Person(String firstName, String lastName, String birthDate) {
this.firstName = firstName;
this.lastName = lastName;
this.birthDate = LocalDate.parse(birthDate);
}
}
MyDurationSerializer
public class MyDurationSerializer extends StdSerializer<Duration> {
private static final long serialVersionUID = 1L;
protected MyDurationSerializer() {
super(Duration.class);
}
#Override
public void serialize(Duration value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.toString());
}
}
MyLocalDateSerializer
public class MyLocalDateSerializer extends StdSerializer<LocalDate> {
private static final long serialVersionUID = 1L;
private DateTimeFormatter FORMATTER = ofPattern("yyyy-MM-dd");
protected MyLocalDateSerializer() {
super(LocalDate.class);
}
#Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(value.format(FORMATTER));
}
}
Spring Data REST HATEOAS representation
curl http://localhost:4000/albums/1/tracks
{ "id" : 1,
"albums" : [ {
"id" : 1,
"title" : "Kind Of Blue",
"artist" : "#{
"id" : 1,
"firstName" : "Miles",
"lastName" : "Davis",
"birthDate" : "1926-05-26"
},
"tracks" : [ {
"id" : 1,
"title" : "So What",
"duration" : "PT9M5S",
"musicians" : [ {
"id" : 1,
"firstName" : "Miles",
"lastName" : "Davis",
"birthDate" : "1926-05-26"
}, {
"id" : 2,
"firstName" : "Bill",
"lastName" : "Evans",
"birthDate" : "1929-09-16"
} ]
}, {
"id" : 3,
"title" : "Blue in Green",
"duration" : "PT5M29S",
"musicians" : [ {
"id" : 1,
"firstName" : "Miles",
"lastName" : "Davis",
"birthDate" : "1926-05-26"
}, {
"id" : 2,
"firstName" : "Bill",
"lastName" : "Evans",
"birthDate" : "1929-09-16"
} ]
} ]
} ],
"artists" : [ {
"id" : 15,
"firstName" : "Miles",
"lastName" : "Davis",
"birthDate" : "1926-05-26"
}, {
"id" : 16,
"firstName" : "Bill",
"lastName" : "Evans",
"birthDate" : "1929-09-16"
} ]
}
Akin to my earlier question, I'm trying to access data in MongoDB using Spring REST.
I have collections of simple Key-Value Pairs and can access those fine.
{
"_id" : ObjectId("5874ab4a19b38fb91fbb234f"),
"roID" : "7ed3f9a6-bb9b-4d16-8d1a-001b7ec40b51",
"Name" : "[REDACTED]"
}
The problem is, these objects are used in another collection that displays a relationship with properties between them, like this:
{
"_id" : ObjectId("5874ab4f19b38fb91fbb6180"),
"[OBJECT CATEGORY A]" : {
"_id" : ObjectId("5874ab4a19b38fb91fbb257b"),
"roID" : "72f8a8b5-71a7-40ac-b1ac-1ffc98a507ba",
"Name" : "[REDACTED]"
},
"[OBJECT CATEGORY B]" : {
"_id" : ObjectId("5874ab4b19b38fb91fbb32a3"),
"roID" : "919446ab-1898-419f-a704-e8c34985f945",
"Name" : "[REDACTED]"
},
"[RELATIONSHIP INFORMATION]" : [
{
"[PROPERTY A]" : [
{
"[VALUE A]" : 5.0
},
{
"[VALUE B]" : 0.0
}
]
},
Properties are somewhere between 8 and 20.
The definition of the first (plain) object in Java looks like this:
#Document(collection="OBJ")
public class Obj {
public Obj(){};
#Id
public String id;
#Field("roID")
public String roID;
#Field("Name")
public String name;
}
The repository class:
#RepositoryRestResource(collectionResourceRel = "OBJ", path = "OBJ")
public interface ObjRepo extends MongoRepository<Obj, String> {
List<Obj> findByName(#Param("name") String name);
}
The question is: how do I access the nested objects? I've tried using LinkedHashMap in place of the Strings for the complex collection, curl only returns "null" when I try to access them. I tried defining a class
public class BITS {
#Id
private String _id;
#Field("roID")
private String roID;
#Field("Name")
private String name;
public BITS(){}
public BITS(String _id,String roID, String name){
this._id = _id;
this.roID = roID;
this.name = name;
}
}
to access these objects, unsuccessfully.
Turns out the class approach was correct, just not well executed.
I've created a plain JSON Collection for testing purposes:
#Document(collection="JSON")
public class JSON {
#Id
public String id;
#Field("squares")
public Square square;
#Field("dots")
public Dot dot;
public JSON(){};
public JSON(String id, Square square,Dot dot){
this.id = id;
this.square = square;
this.dot = dot;
};
}
Square.java
public class Square {
private String id;
private int x;
private int y;
public Square(){};
public Square(String id,int x, int y){
this.id = id;
this.x = x;
this.y = y;
};
public Map<String, Integer> getSquare()
{
Map<String, Integer> res = new HashMap<>();
res.put("x", x);
res.put("y", y);
return res;
}
}
(Dot is the same, just for the test)
So it's just about remodeling the desired response exactly, despite it being in that format in the database already.
If anyone could point me to where I can remove the clutter from the response tho, that would be nice. Currently looks like this:
"_embedded" : {
"JSON" : [ {
"square" : null,
"dot" : {
"dot" : {
"x" : 4,
"y" : 3
}
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON/58ac466160fb39e5e8dc8b70"
},
"jSON" : {
"href" : "http://localhost:8080/JSON/58ac466160fb39e5e8dc8b70"
}
}
}, {
"square" : {
"square" : {
"x" : 12,
"y" : 2
}
},
"dot" : null,
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON/58ac468060fb39e5e8dc8b7e"
},
"jSON" : {
"href" : "http://localhost:8080/JSON/58ac468060fb39e5e8dc8b7e"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/JSON"
},
"profile" : {
"href" : "http://localhost:8080/profile/JSON"
}
},
"page" : {
"size" : 20,
"totalElements" : 2,
"totalPages" : 1,
"number" : 0
}
}