GSON how to exclude a property at second level of serialization - java

I building a web api with Jersey and JaxRs and I am searching a way to prevent GSON from serializing à circular reference. I have found a lot of answers saying that in the case of a "parent > child" relation, we could add the transient keyword to the parent property so it won't be serialized. However, my situation is not this kind of relation and I haven't found how to achieve what I need.
I have a situation where one User have a list of many other Users (their friends):
public class User {
public int id;
public String name;
public List<User> friends;
}
Now, let's suppose that user1 is friend with user2 so they are both added in each others friend list :
user1.friends.add(user2);
user2.friends.add(user1);
This causes a circular reference when serializing (and eventually a StackOverflowException). Indeed, user1's friends contain user2, and user2 friends contains user1, who contains user2.... and so on !
However I cannot just make transient the friends property of the User class, because I need those friends names and ids. I would rather like the friend property to be excluded at the second level of serialization (by serializing the name and id of each friends, but not the friends' friends).
Here's an exemple of a serialized JSON I would need :
[
{
"id":1,
"name":"user1"
"friends": [
{
"id":2,
"name": "user2"
}
]
},
{
"id":2,
"name":"user2"
"friends": [
{
"id":1,
"name": "user1"
}
]
}
]
How could I achieve this ?
Thanks a lot in advance !

Related

How to handle self-reference in Spring Data JPA with Spring Boot 2?

I have a Spring Boot 2 application in which I have the following User entity:
#Data
... JPA and other annotations
class User {
... many fields including Integer id
#ManyToOne(fetch = FetchType.LAZY)
public User createBy;
#ManyToOne(fetch = FetchType.LAZY)
public User updateBy;
}
Now, the main problem I am facing right now is the self-reference of User (from User) and this either is causing StackOverflow Exceptions or InvalidDefinitionException depending on the certain annotations I am using on User. This issue is very common and several solutions are discussed over the internet which are:
1. Annotate both fields with #JsonBackReference
Annotating with #JsonBackReference omits the updateBy and createBy fields altogether, meaning I am not getting them when desired in my API responses.
2. Annotate class with #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") or None.class or IntSequenceGenerator.class or UUIDGenerator.class
This approach works fine, until the serializer finds the same user object somewhere down the json and instead of putting it up there again, it puts a reference of that object based on the class selected above. e.g.
[
{"id": 1, ..., "createBy": {"id": 2, ...},
2 // <- Let's ignore how user 1 is created by user 2 and notice that instead of a user object, I get an integer ID reference.
]
This means, the client parsing this data will often there to be an object and not a number, causing parsing errors.
3. Implementing a custom serializer (or extending an existing one)
Now, I am really unsure if this is the right way to go about achieving my goals (mentioned below). But if this is the right approach, how would I go about handling this self-reference?
GOALS:
Serialize the data so that, at least certain fields in the child object (user) are passed back, preventing further recursive calls.
{
"id": 1, "name": "old user", .. many other fields .., "createBy": {id: 2, "name": "2nd user"}
}
When the client sends a user object as request body, the application needs only the id of the child entity and not the whole object, as below:
{
"name": "new user", ...., "createBy": {id: 1}
}
I know that self-referencing is integral to ORMs and there are a lot of use cases for self-referencing. But how do professional developers/applications handle this issue, especially in Spring Framework? If a custom serializer is the only way to go, how do I make it function appropriately?
Also, is it advisable to exclude these fields (createBy and updateBy) from EqualsAndHashCode and ToString methods?

JaVers - how to add extra fields to reference entity in JSON

I was trying to add JaVers library to my current project. And I wondered Is there any ways to add to reference entity fields some field which will be shown instead of Id when I fetch changes?
For example snapshot of User class:
{
"owner": {
"entity": "Owner",
"cdoId": 1
},
"username": "TMP",...
}
and if I change Owner reference and fetch for changes, I will get:
ReferenceChange{ 'owner' changed from 'Owner/1' to 'Owner/2' }
What I want is some thing like:
{
"owner": {
"entity": "Owner",
"cdoId": 1
"cdoName": "OWN"
},
"username": "TMP",...
and changes like this:
ReferenceChange{ 'owner' changed from 'OWN' to 'FOO' }
Is there any way to achieve this? I`m using Javers 3.11.3
You can't add more fields to a Reference. In JaVers, a Reference is just a global identifier of an entity. Try to describe the problem you have, maybe there is a better solution than changing javers-core model.

In a java REST API, using PATCH vs PUT to update an entity

I am about to start development on a new rest api in Java.
My question is about the use of PATCH - Why?
Lets say, we have an entity named Address.java
public class Address {
#Id
private Long id
#NotNull
private String line1;
private String line2; //optional
#NotNull
private String city;
#NotNull
private String state;
}
To create a new Address, I would do this http request:
POST http://localhost:8080/addresses
with the following request:
{
"line1" : "mandatory Address line 1",
"line2" : "optional Address line 2",
"city" : "mandatory City",
"state" : "cd"
}
Assume the record created has an id 1
The corresponding #RestController AddressResource.java will have this method :
#PostMapping(value = "/addresses")
public ResponseEntity<Address> create(#valid Address newAddress) {
addressRepo.save(newAddress);
}
#valid will ensure the entity is valid before storing the data into the table.
Now assume, I move from my apartment above to a house down the street. If I use a PATCH, it becomes
PATCH http://localhost:8080/addresses/1
with request payload:
{
"line1" : "1234 NewAddressDownTheStreet ST",
"line2" : null
}
The corresponding #RestController method would be :
#PatchMapping(value = "/addresses/{id}")
public ResponseEntity<Address> patchAddress(#PathVariable Long id, Address partialAddress)
{
Address dbAddress = addressRepo.findOne(id);
if (partialAddress.getLine1() != null) {
dbAddress.setLine1(partialAddress.getLine1());
}
if (partialAddress.getLine2() != null) {
dbAddress.setLine2(partialAddress.getLine2());
}
if (partialAddress.getCity() != null) {
dbAddress.setCity(partialAddress.getCity());
}
if (partialAddress.getState() != null) {
dbAddress.setState(partialAddress.getState());
}
addressRepo.save(dbAddress)
}
Now if you query the table, won't my address be ?
"line1" : "1234 NewAddressDownTheStreet ST",
"line2" : "optional Address line 2", <-- INCORRECT. Should be null.
"city" : "mandatory City",
"state" : "cd"
As can be seen, the above updates results in an incorrect value for line2.
This is because in java all instance variables in the Address class are initialized to null (or default initial values if they are primitives) when a class is instantiated. So there is no way to distinguish between line2 being changed to null from the default value.
Question 1) Is there a standard way to work around this?
Another disadvantage is that, I cannot use #Valid annotation to validate the request at the entry point - coz it is only a partial. So, invalid data could get into the system.
For example, imagine there was additional field with the following definition:
#Min(0)
#Max(100)
private Integer lengthOfResidencyInYears,
And the user accidentally typed 190 (when they really meant 19 years), it would not fail.
Instead of PATCH, if I had used PUT, the client would need to send the complete address object.
This has the advantage that I can use #Valid to ensure that the Address is indeed valid
If one makes the premise that a GET MUST always be done before doing any updates, why wouldn't one use PUT over PATCH?
Am I missing something?
Aside
My conclusion is that developers using dynamically typed languages are the proponents of using PATCH as I cannot see any benefit to using it from a statically typed language line Java or C#. It just seems to add more complexity.
Using PATCH to upload a modified version of an existing object is almost always problematic for exactly the reason you have outlined. If you want to use PATCH with JSON, I strongly suggest you follow either RFC 6902 or RFC 7396. I won't speak to 7396 because I'm not that familiar with it, but to follow 6902 you would define a separate resource for PATCH operations. In the example you gave, it would look like:
PATCH http://localhost:8080/addresses/1
[
{ "op": "replace", "path": "/line1", "value": "1234 NewAddressDownTheStreet ST" },
{ "op": "remove", "path": "/line2" }
]
You would then process this, making a new entity object that started at the current server state and applied the changes in the PATCH. Run validation on the new entity object. If it passes, push it to the data layer. If it fails, return an error code.
If PUT doesn't add too much overhead, it is a good idea. Idempotency is a nice thing to have. The tradeoff is that you're pushing more data over the wire. If your resource is not large and not accessed often, that's maybe not such a big deal. If your resource is large and is accessed often, that can start to add significant overhead. Of course, we can't tell you the tipping point.
You also seem to have completely tied your resource model to your database model. Good database table design and good resource design often look very different for non-trivial projects. I understand that many frameworks drive you in that direction, but you if you haven't seriously considered decoupling them, you might want to.

Modify #OneToMany entity in Spring Data Rest without its repository

In my project I use object of type A which has OneToMany relation (orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER) to objects of type B. I need SpringDataRest (SDR) to store complete full A object with its B objects (children) using single one POST request. I tried several combinations in SDR, the only one which worked for me, was to create #RepositoryRestResource for object A and to create #RepositoryRestResource also for object B, but mark this (B) as exported=false (if I did not create repository out of object B at all, it would not work -> just A object would be stored on single POST request, but not its children (#OneToMany relation) of type B; the same outcome occurs if exported=false is omitted for B repository).
Is this ok and the only way how to achieve it (single POST request with storing all objects at once)?
The reason I'm asking, in my previous example, I have to (I would like to) control all objects "lifecycle" by using A's repository. I am ok with it, because A->B relation is composition (B does not exists outside of A). But I have serious problem of editing (also removing) one certain object of type B by SDR using its parent repository (since object B doest not have its own repository exported). Maybe, this is not possible by definition. I have tried these solutions:
PATCH for "/A/1/B/2" does not work -> method not allowed (in headers
is "Allow: GET, DELETE") -> so, also PUT is out of question
Json Patch would not work either - PATCH for "/A/1" using json patch
content-type [{"op": "add", "path": "/B/2", ....}] -> "no such index
in target array" - because Json Patch uses scalar "2" after "array"
as a index to its array. This is not practical in Java world, when
relations are kept in Set of objects - indexing has no meaning
at all.
I could export repository (exported=true) of object B for
manipulating it "directly", but this way I would loose ability to
store the whole object A with its B objects at one single POST
request as I have mentioned before.
I would like to avoid sending the whole A object with one single tiny modification of its B object for PUT, if possible.
Thank you.
I managed to change the child entity as follows. As a sample I used the following entities:
#Entity
#Data
#NoArgsConstructor
public class One {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(cascade = ALL)
private List<Many> manies = new ArrayList<>();
}
#Entity
#Data
#NoArgsConstructor
public class Many {
public Many(String name) {
this.name = name;
}
#Id
#GeneratedValue
private Long id;
private String name;
}
I just have a repository for One exposed.
(My examples use the excellent httpie - CLI HTTP client)
Removing an item using json patch
This example will remove the second item in the manies list. You could use #OrderColumn to make sure that you can rely on the order of list items.
echo '[{"op":"remove", "path":"/manies/1"}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v
PATCH /ones/1 HTTP/1.1
Content-Type: application/json-patch+json
[
{
"op": "remove",
"path": "/manies/1"
}
]
Replacing the entire list using json patch
This sample replaces the list with the array specified in the value.
echo '[{"op":"add", "path":"/manies", "value":[{"name":"3"}]}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v
PATCH /ones/1 HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
[
{
"op": "add",
"path": "/manies",
"value": [
{
"name": "3"
}
]
}
]
Adding an item to the list using json patch
This sample adds an item to the end of a list. Also here the client just needs to know the length of the list before the update. So the order does not really matter here.
echo '[{"op":"add", "path":"/manies/-", "value":{"name":"4"}}]' | http PATCH :8080/ones/1 Content-Type:application/json-patch+json -v
PATCH /ones/1 HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
[
{
"op": "add",
"path": "/manies/-",
"value": {
"name": "4"
}
}
]
Hope this helps.

How should I store this set of data into a Mongo Collection

I have the following Data Structure
{
"routers": [
{
"name": "",
"ip address": "",
}
],
"devices": [
{
"name": "",
"mac address": [
<address>,
<address>,
<address>
],
}
],
"connection": [
{
"source":"<name>",
"destination":"<name>",
}
]
}
I want to store this in a Mongo Database (in a Java web application). I just need some opinions on how to structure this.
Do I put all of this JSON into one Mongo Collection, named "Environment"?
I want to map the data to Java Objects, do I have three separate Objects "Router","Device" and "Connection" OR do I make one Java object "Environment" and have the three Java objects listed above in that particular object? I'm feeling like this second option is the best.
I'm pretty much looking for some advice on the best way to design this, instead of trying to change it later on.
Not that it matters, but I am going to also be using Jongo
EDIT to show potential class:
private class Environment {
private List<Router>;
private List<Device>;
private List<Connection>
...
}
There would be multiple "Environment", essentially what we are building is a configuration management system so there are different "Configurations" of Environments.
I would do lots of documents in 1 collection. I find it easier to search that way and easier to write pojo-like code against.
Your data modelling question has been answered before: Mongodb: multiple collections or one big collection w/ index
I would use any of the very many JSON utility libs (e.g. GSON) for Java.
Create a pojo class that has all of the fields you require in the data structure:
private List<Router> routers;
...
private static class Router {
private String name;
...
}
Create getters/setters if you need to
Use your utility to convert to/from, example of a utility class below
import com.google.gson.Gson;
import com.google.gson.JsonParser;
private static Gson gson = new Gson();
private static final JsonParser parser = new JsonParser();
public static <T> T toPojoObject(String json, Class<T> clazz, Gson aGson) {
return aGson.fromJson(createJSONObject(json).getAsJsonObject(), clazz);
}
public static JsonElement createJSONObject(String json) {
return parser.parse(json);
}
(OK not the most wonderful example of using GSON, but just illustrating the point)
Making one java object "environment" and including others in it will be better. e.g I am using JPA based object datastore mapper and in that you can use "#embedded" to embed other objects in that entity if you have all the objects embedded in one entity you can persist all the objects using a single call to database otherwise you have to hit the database thrice for persisting the three objects.

Categories

Resources