Jackson Polymorphism and #JsonTypeInfo usage - jackson

Based on the following JSON:
{"items": [
{
"post_id": 17168289,
"count": 190
}
]}
And:
{"items": [
{
"tag_id": 17168289,
"count": 190
}
]}
I want to create a Tag object and a Post object that both extend an Item object.
How can I use the #JsonTypeInfo annotation to pick up the name of the *_id field so Jackson creates the correct object?
Here's where I am so far:
#JsonSubTypes({
#JsonSubTypes.Type(value=Post.class, name="post_id")
#JsonSubTypes.Type(value=Tag.class, name="tag_id")
})
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
public class Item {
public int count;
}

Related

How to returning only as string column in json response object from eloquent relationship using eager loading

I'm trying to figure out how to eager load data as a string instead of array object from a related table.
I have 3 models and here are the relations
Product.php
class Product extends Model
{
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
ProductTag.php
class ProductTag extends Model
{
public function Product()
{
return $this->belongsTo(Product::class);
}
}
Category.php
class Category extends Model
{
public function Product()
{
return $this->hasMany(Product::class);
}
}
I've tried doing it like so:
public function tag(){
return $this->hasMany(ProductTag::class)
->selectRaw('GROUP_CONCAT(tag) as tag,id')
->groupBy('id');
}
public static function list(){
$result = Category::with(['Product'=>function($q){
$q->with(['tag'=>function($q1){
$q1->first();
}]);
}])->get();
}
Here is the reponse:
{
"data": {
"categories": [
{
"id": 1,
"category": "test 1",
"product": [
{
"id": 46,
"name": "test prod 1",
"tag": []
},
{...}
]
},
{
"id": 2,
"category": "test 2",
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
},
{...}
]
},
{
"id": 3,
"category": "test 3",
"product": []
}
]
}
}
The Response is as expected except tag array so, instead of an array named "tag" can I get "product_tag" within the "product" array
"product": [
{
"id": 45,
"name": "test prod 2",
"tag": [
{
"product_tag": "Test1, test12, test123"
}
]
}
]
Here is what I want it to look like:
"product": [
{
"id": 45,
"name": "test prod 2",
"product_tag": "Test1, test12, test123"
}
]
Is it possible and any smart way of doing this in Laravel using Eloquent?
Simple :)
Btw, if you can - rename product_tag in response to tag_line or same - it's not right way to take same name for relation and mutator.
class Product extends Model
{
public function getTagLineAttribute()
{
//tag - is "name" field in ProductTag
return $this->ProductTag->pluck('tag')->implode(',');
}
public function ProductTag()
{
return $this->hasMany(ProductTag::class);
}
public function Category()
{
return $this->belongsTo(Category::class);
}
}
//eager loading with tags
Product::with('ProductTag')->find(..)->tagLine;

Use a projection to modify only one field but keeping default for all others

First of all I'd like to say I love what i've seen so far from Spring Data JPA and Spring Data REST. Thanks a lot to all people involved.
Problem description
I have an entity model similar to the classes below. One parent and two different child entities referencing the parent als a ManyToOne Assoziation. For one of the childs i like to have the default rendering of all its properites and links as it is when no projection is applied to the parent.
The other child should be mapped to a simple string array containing only the id or some specific field.
Code and example JSONs
#Entity
public class Parent {
#Id
private Long id;
private String parentValue;
#OneToMany(mappedBy = "parent")
private List<Child1> child1;
#OneToMany(mappedBy = "parent")
private List<Child2> child2;
// ... getters and setters
}
#Entity
public class Child1 {
#Id
private Long id;
private String child1Value;
#ManyToOne
Parent parent;
// ... getters and setters
}
#Entity
public class Child2 {
#Id
private Long id;
#ManyToOne
Parent parent;
}
the response when getting the collection resource of parent is this
{
"_embedded": {
"parents": [
{
"parentValue": "Parent1",
"child1": [
{
"child1Value": "Child1",
"_links": {
"parent": {
"href": "http://localhost:8080/parents/1"
}
}
}
],
"child2": [
{
"_links": {
"parent": {
"href": "http://localhost:8080/parents/1"
}
}
}
],
// removed remaining json to shorten the example
}
But what i like to achieve is the following JSON
{
"_embedded": {
"parents": [
{
"parentValue": "Parent1",
"child1": [
{
"child1Value": "Child1",
"_links": {
"parent": {
"href": "http://localhost:8080/parents/1"
}
}
}
],
"child2": [1],
What i tried so far
Added an excerptProjection to the ParentRepository:
#RepositoryRestResource(excerptProjection = ParentRepository.ArrayProjection.class)
public interface ParentRepository extends PagingAndSortingRepository<Parent, Long>{
public interface ArrayProjection {
String getParentValue();
List<Child1> getChild1();
#Value("#{target.child2.![id]}")
List<Long> getChild2();
}
}
Edited: In the first version of the question, the Projection was incorrect regarding the return type of getChild1(), as it should return the complete collection not only one element. Thanks #kevvvvyp for still trying to help.
The result is similar to what i want, but the links on the Child1 property are missing now:
{
"_embedded": {
"parents": [
{
"child2": [
2
],
"child1": {
"child1Value": "Child1"
},
"parentValue": "Parent1",
// removed remaining json to shorten example
Also the approach with the excerptProjection means i'd have to change the projection everytime the entity changes. Which probalby won't happen to much but that means somebody will forget to change it in the future ;-)
I think a projection is the right way to go here, what about...
interface ArrayProjection {
#JsonIgnore
#Value("#{target}")
Parent getParent();
default String getParentValue(){
return this.getParent().getParentValue();
}
default Child1 getChild1(){
//TODO what are we actually trying to return here? Given we join the parent to
// the child on id & id is the primary key on both entities can we ever get more than one child?
return CollectionUtils.firstElement(this.getParent().getChild1());
}
default List<Long> getChild2() {
return this.getParent().getChild2().stream()
.map(Child2::getId)
.collect(Collectors.toList());
}
}
Response...
GET http://localhost:8080/api/parents
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Transfer-Encoding: chunked
Date: Wed, 31 Mar 2021 21:08:54 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"_embedded": {
"parents": [
{
"parentValue": "Parent1",
"child1": {
"child1Value": "Child1"
},
"child2": [
1
],
"_links": {
"self": {
"href": "http://localhost:8080/api/parents/1"
},
"parent": {
"href": "http://localhost:8080/api/parents/1{?projection}",
"templated": true
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/parents"
},
"profile": {
"href": "http://localhost:8080/api/profile/parents"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
Response code: 200; Time: 713ms; Content length: 724 bytes
In reponse to your concern, if we code using default method's we will see a compile error when that entity changes. It might be possible to use a class projection instead.
Also I would consider if we really want to return a projection by default... it might confuse a client who then tries to create/update a parent (this is perhaps why you've kept the id's hidden by default?).
Sample - https://github.com/kevvvvyp/sdr-sample
Update
Another (more complex) way to acheive this could be to make use of Jackson's SimpleBeanPropertyFilter, you could customise the JSON response based on some value (the entity class, a value in a particular field, an annotation etc).

Type mismatch during Gson deserialize json response with kotlin

I am trying to serialize the json response below, but I am unsure how to do it.
This is the Json my backend returns:
[
{
"title": "Dummy section, should not be seen",
"type": "dummy_test",
"metadata": []
},
{
"title": "Title1",
"type": "categories_products",
"metadata": [
{
"id": "1272"
}
]
},
{
"title": "Title2",
"type": "categories_products",
"metadata": [
{
"id": "996"
}
]
}
]
This is my ExploreItem class:
data class ExploreItem(
#SerializedName("metadata") val metadata: List<Metadata> = listOf(),
#SerializedName("title") val title: String = "",
#SerializedName("type") val type: String = ""
) {
enum class ExploreItemType(val value: String) {
#SerializedName("unknown")
UNKNOWN("unknown"),
#SerializedName("other_companies")
OTHER_COMPANIES("other_companies"),
#SerializedName("categories_products")
CATEGORIES_PRODUCTS("categories_products"),
#SerializedName("popular_categories")
POPULAR_CATEGORIES("popular_categories")
}
}
data class Metadata(
#SerializedName("id") val id: String = ""
)
And now I am trying to serialize it in the repository like this:
Serializer.defaultJsonParser.fromJson(response.body!!.string(),ExploreItem::class.java )
but it doesn't work because it's expecting a list of ExploreItem. How can I rewrite the serializer expression to parse it into a list?
From your error
Type mismatch. Required:List Found:ExploreItem!
Post errors is very important, Gson is telling you that it wants a List and not an object of ExploreItem.
In other words, you are telling to Gson with the call Serializer.defaultJsonParser.fromJson(response.body!!.string(),ExploreItem::class.java )
"Hey Gson, from the string I want an object ExploreItem", and Gson is telling you "Hey my friend, you string start with [ ] for sure it is a list of something and not a single object."
You need to pass in the Serializer.defaultJsonParser.fromJson(response.body!!.string(),List<ExploreItem>::class.java)
P.s: I'm not sure about the Kotlin syntax

How can I return child objects in a many-to-many relationship with Dropwizard/Hibernate/Swagger without recursion?

I'm using Dropwizard and Swagger to create a REST API to manage access to various objects. Two of these objects are involved in a many-to-many relationship, for example:
public class Puppy implements Serializable {
private Long id;
private String name;
#ManyToMany(targetEntity = Trick.class)
#JoinTable(
name="puppies_tricks",
joinColumns=#JoinColumn(name="puppy_id"),
inverseJoinColumns=#JoinColumn(name="trick_id"))
private List<Trick> tricks;
#JsonProperty("tricks")
public List<Trick> getTricks() { return this.tricks; }
...
}
public class Trick implements Serializable {
private Long id;
private String name;
#ManyToMany(targetEntity = Puppy.class)
#JoinTable(
name="puppies_tricks",
joinColumns=#JoinColumn(name="trick_id"),
inverseJoinColumns=#JoinColumn(name="puppy_id"))
private List<Puppy> puppies;
#JsonProperty("puppies")
public List<Puppy> getPuppies() { return this.puppies; }
...
}
And suppose the actual data looks something like:
# Tricks:
[{ id: 1, name: 'Roll over' },
{ id: 2, name: 'Play dead' },
{ id: 3, name: 'Steal second' }]
# Puppies:
[{ id: 1, name: 'Flopsy' },
{ id: 2, name: 'Mopsy' },
{ id: 3, name: 'Cottontail' }]
# Puppies_Tricks
[{ puppy_id: 1, trick_id: 1 },
{ puppy_id: 1, trick_id: 2 },
{ puppy_id: 2, trick_id: 2 },
{ puppy_id: 2, trick_id: 3 }]
So when I GET /puppy/1 I want to get a structure like:
{
"id": 1,
"name": "Flopsy",
"tricks": [
{ "id": 1, "name": "Roll over"},
{ "id": 2, "name": "Play dead" } ]
}
but what I'm actually getting is a nested array:
{
"id": 1,
"name": "Flopsy",
"tricks": [
{ "id": 1, "name": "Roll over", "puppies": [ { "id": 1, "name": "Flopsy" }, ...},
{ "id": 2, "name": "Play dead", "puppies": [...] } ]
}
Is there some way to tell Jackson/Dropwizard to stop descending through tricks to when getting puppies, and vice versa?
Dropwizard 1.0.6, Java 1.8.
Jackson has support of object-graph serialization through the #JsonManagedReference #JsonBackReference annotations. See the documentation here. You can find a simple example here.
You should really read more carefully the documentation of the tools you're using.
There is already an answer that summarizes what approaches are available.
#JsonIgnore
#JsonProperty("puppies")
public List<Puppy> getPuppies() { return this.puppies; }
will stop infinite loop
My solution here was to create a separate PuppyCore class that implements serialization but isn't persisted. This includes basic Puppy info (id, name). Similarly a TrickCore has id and name only.
Then Puppy includes Set tricks, with this getter:
public Set<TrickCore> getTricks();
which returns a list of TrickCore objects – these don't have the Puppy list, and so halts the recursion. A similar pattern on Trick returns Set<PuppyCore> for getPuppies().

Generate links for collection resources for a specific single resource

I wrote a custom controller to handle a GET http://localhost:54000/api/v1/portfolios/{id}/evaluate request.
#RequestMapping(value = "/portfolios/{id}/evaluate", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> evaluate(#PathVariable Long id) {
Portfolio portfolio = portfolioService.evaluate(id);
if (portfolio == null) {
return ResponseEntity.notFound().build();
}
Resource<Portfolio> resource = new Resource<>(portfolio);
resource.add(entityLinks.linkForSingleResource(Portfolio.class, id).withSelfRel());
return ResponseEntity.ok(resource);
}
The current response is
{
"summary" : {
"count" : 24.166666666666668,
"yield" : 0.14921630094043895,
"minBankroll" : -6.090909090909091,
"sharpeRatio" : 0.7120933654645042,
"worstReturn" : -2.4545454545454533,
"losingSeason" : 3,
"return" : 3.6060606060606077
},
"_links" : {
"self" : {
"href" : "http://localhost:54000/api/v1/portfolios/4"
}
}
}
but I would like to add collection resources (summaries and systems) linked to that portfolio:
{
"summary": {
"count": 24.166666666666668,
"yield": 0.14921630094043895,
"minBankroll": -6.090909090909091,
"sharpeRatio": 0.7120933654645042,
"worstReturn": -2.4545454545454533,
"losingSeason": 3,
"return": 3.6060606060606077
},
"_links": {
"self": {
"href": "http://localhost:54000/api/v1/portfolios/4"
},
"portfolio": {
"href": "http://localhost:54000/api/v1/portfolios/4"
},
"summaries": {
"href": "http://localhost:54000/api/v1/portfolios/4/summaries"
},
"systems": {
"href": "http://localhost:54000/api/v1/portfolios/4/systems"
}
}
}
I did not find a way to generate those links with the RepositoryEntityLinks entityLinks object
You can always do something like this:
entityLinks.linkForSingleResource(Portfolio.class, id).slash("systems").withRel("systems");
And if your systems endpoint is implemented in a custom controller method you can use the ControllerLinkBuilder to generate a link to your controller method. Lets say you implemented the getSystems method with id parameter in MyControllerClass - then you can generate the link like this (linkTo and methodOn are static methods in ControllerLinkBuilder):
linkTo(methodOn(MyControllerClass.class).getSystems(id)).withRel("systems");