Can I embed the Many side of a Spring Data Rest/JPA Bi-directional OneToMany as an Array when GETing the One side - embed

I am exploring Spring Data Rest with a bidirectional mapping. I have two tables One & Many for clarity
One table
#OneToMany( mappedBy = "one")
#JsonBackReference
List<Many> many;
Many table
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonManagedReference
#RestResource(exported = false)
#JoinColumn(name = "one_id")
One one;
As expected when I use localhost:8080/manies the One reference is embedded
"manies": [
{
"rack": "GF",
"shelf": "2",
"one": {
"producer": "Escarpment",
"name": "Kupe"
},
"_links": {
"self": {
"href": "http://localhost:8080/manies/1"
},
"many": {
"href": "http://localhost:8080/manies/1"
}
}
},
What I am wondering is there any way to do the same with localhost:8080/ones i.e. embed an array of Many
When I try I can only get a link returned
"ones": [
{
"producer": "Escarpment",
"name": "Kupe",
"_links": {
"self": {"href": "http://localhost:8080/ones/1"},
"one": {"href": "http://localhost:8080/ones/1"},
**"many": {"href": "http://localhost:8080/ones/1/many"}**
}
},

Related

Is there any way to manipulate json response in spring boot?

My project is about a shopping application build with Spring boot. When I get the shopping cart from the endpoint. It will return JSON like this
{
"shoppingCartCode": 2,
"productDetailList": [
{
"productDetailCode": 5,
"price": 21000.0,
"dateManufacture": "2021-09-23",
"quantity": 10,
"colorName": "Pink",
"warranty": 2,
"product": {
"prodCode": 2,
"prodName": "Z Flod 10",
"description": "Z Flod 10 Flippable then Break up",
"brand": {
"brandName": "Samsung",
"imageList": []
},
"shop": {
"shopCode": 5,
"shopName": "Montri Phone",
"shopDescription": "Strict security",
"type": "SELLER",
"imageList": []
}
},
"imageList": [
{
"imageName": "i-agree.png"
},
{
"imageName": "i-agree1.png"
},
{
"imageName": "i-agree2.png"
},
{
"imageName": "i-agree3.png"
}
]
}
]
}
I want to manipulate the imageList to like this
"imageList": ["i-agree.png","i-agree1.png","i-agree2.png","i-agree3.png"]
Is there any way to manipulate JSON before response?
or it might need POJO to wrap it up before response again?
You can achieve this with a custom serializer as follows:
public class ImageListConverter extends StdConverter<List<Image>, List<String>> {
#Override
public List<String> convert(List<Image> images) {
return images.stream().map(User::getImageName).collect(Collectors.toList());
}
}
Now you just need to annotate the imageList property:
public class ProductDetail {
#JsonSerialize(converter = ImageListConverter.class)
List<Image> imageList;
}
The other option would be changing List<Image> to List<String> and do the conversion when creating ProductDetail. It really depends on how you use ProductDetail elsewhere.

How to get relationship data with spring boot rest api?

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":{
//...
}
},
// ...
]
}
}

Unity JSON Serializer not allowing for calling JSON Fields n JSON Objects

The have an issue where I am unable to call nested JSON Objects from a scraped website. The scraping process works prefectly, but the JSON Serializing part is the only issue. My code is shown below:
private void GetHtmlAsync()
{
var url = "https://opentdb.com/api.php?amount=10";
var httpClient = new HttpClient();
var html = httpClient.GetStringAsync(url);
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(MyDetail));
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(html.Result));
stream.Position = 0;
MyDetail dataContractDetail = (MyDetail) jsonSerializer.ReadObject(stream);
text.text = "" + dataContractDetail.results[1];
//text.text = string.Concat("Test: ", dataContractDetail.question, " " + dataContractDetail.correct_answer);
}
public class MyDetail
{
[DataMember]
public Dictionary<string, questions> results
{
get;
set;
}
public class questions
{
public string question { get; set; }
public string correct_answer { get; set; }
}
[DataMember]
public string response_code
{
get;
set;
}
}
This code is the code that does not work, in that I try to call the first object in results by doing "results[1]", which returns an error after I attach, say, "question" to it by doing "results[1].question". This syntax seems reasonable, so I don;t understand why it is not working. My JSON File is shown below:
{
"response_code": 0,
"results": [
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "What is the name of the virus in "Metal Gear Solid 1"?",
"correct_answer": "FOXDIE",
"incorrect_answers": [
"FOXENGINE",
"FOXALIVE",
"FOXKILL"
]
},
{
"category": "Geography",
"type": "multiple",
"difficulty": "easy",
"question": "What is the official language of Costa Rica?",
"correct_answer": "Spanish",
"incorrect_answers": [
"English",
"Portuguese",
"Creole"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "In Fallout 4, which type of power armor is first encountered in the early mission "When Freedom Calls" in a crashed Vertibird?",
"correct_answer": "T-45",
"incorrect_answers": [
"T-51",
"T-60",
"X-01"
]
},
{
"category": "Politics",
"type": "boolean",
"difficulty": "medium",
"question": "George W. Bush lost the popular vote in the 2004 United States presidential election.",
"correct_answer": "False",
"incorrect_answers": [
"True"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "In "Halo 2", what is the name of the monitor of Installation 05?",
"correct_answer": "2401 Penitent Tangent",
"incorrect_answers": [
"343 Guilty Spark",
"031 Exuberant Witness",
"252 Biodis Expolsion"
]
},
{
"category": "Entertainment: Books",
"type": "multiple",
"difficulty": "medium",
"question": "The book "Fahrenheit 451" was written by whom?",
"correct_answer": "Ray Bradbury",
"incorrect_answers": [
"R. L. Stine",
"Wolfgang Amadeus Mozart",
"Stephen King"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "multiple",
"difficulty": "hard",
"question": "In "Rick and Morty", from which dimension do Rick and Morty originate from?",
"correct_answer": "C-137",
"incorrect_answers": [
"J1977",
"C-136",
"C500-a"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "hard",
"question": "In which game did the character "Mario" make his first appearance?",
"correct_answer": "Donkey Kong",
"incorrect_answers": [
"Super Mario Bros.",
"Super Mario Land",
"Mario Bros."
]
},
{
"category": "Entertainment: Film",
"type": "multiple",
"difficulty": "hard",
"question": "What was Humphrey Bogart's middle name?",
"correct_answer": "DeForest",
"incorrect_answers": [
"DeWinter",
"Steven",
"Bryce"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "boolean",
"difficulty": "medium",
"question": "In "Avatar: The Last Airbender" and "The Legend of Korra", Lavabending is a specialized bending technique of Firebending.",
"correct_answer": "False",
"incorrect_answers": [
"True"
]
}
]
}
There are many issues in your code. I don't know all the libraries you are using but here is how I would do it.
First of all you start a GetStringAsync but you continue immediately without waiting for results. I don't know all the libraries you are using ofcourse maybe it is supposed to be like that?
However I would rather use Unity's UnityWebRequest.Get
private void GetHtmlAsync()
{
StartCoroutine(DownloadJson());
}
private IEnumerator DownloadJson()
{
var url = "https://opentdb.com/api.php?amount=10";
using(var uwr = UnityWebRequest.Get(url))
{
// send the request and wait for result
yield return uwr.SendWebRequest();
// Check for success!
if(uwr.isNetworkError || uwr.isHttpError || !string.IsNullOrWhiteSpace(uwr.error))
{
Debug.LogError($"Download failed with {uwr.responseCode} reason: {uwr.error}", this);
yield break;
}
var json = uwr.DownloadHandler.text;
// ... se below
}
}
Again I don't know your JSON library but your class seems not to match the JSON data structure which would be (by simply jamming it through json2csharp)
[Serializable]
public class Result
{
public string category;
public string type;
public string difficulty;
public string question;
public string correct_answer;
public List<string> incorrect_answers;
}
[Serializable]
public class MyDetail
{
public int response_code;
public List<Result> results;
}
for Unity I would use [Serializable] and also remove all the {get;set} in order to not use properties but fields.
Then you could simply use Unity's JsonUtility
...
MyDetail dataContractDetail = JsonUtility.FromJson<MyDetail>(json);
Then as mentioned in the comments note that array indices in c# are 0-based so the first element would be
var firstResult = dataContractDetail.results[0];
Now the question is what do you want to see on your text? The firstResult is no string but rather a class having various members! You could e.g. want to display the question like
text.text = firstResult.question;

How to get json data into apex salesforce?

I have a json like this, which i am getting in the response from http call
{
"offset": 0,
"limit": 50,
"objects": [
{
"id": "59118fb6e4b0168ec4b56692",
"modifiedDate": 1494323126886,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Abhimanyu",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "absinghrathore127#gmail.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
},
{
"id": "590d5813e4b03a8336fa1642",
"modifiedDate": 1494046739619,
"requestedIds": null,
"mergedIds": [],
"properties": {
"name": [
{
"value": "Tim Archer",
"metadata": {}
}
],
"company": [],
"title": [],
"email": [
{
"value": "tim#avocado.com",
"metadata": {}
}
]
},
"state": "ACTIVE"
}
],
"size": 2
}
and i am able to get objects from json via this following code :
String s = res.getBody();
Map<String,Object> jsonMap = (Map<String, Object>)JSON.deserializeUntyped(s);
String jsonSubset = JSON.serialize(jsonMap.get('objects'));
What i need is the value of name and email in some variable.
Please help me out in this!!
This is going to be a tedious task but once you've classified your all data into appropriate Wrapper classes then it's fairly simple and easy to maintain.
First thing is to define your MainWrapper class. This will contain all the at it's own level. If it has any Object as key-pair then we need to make sure to include it as a List<>. So This is how your MainWrapper should be:
public class MainWrapper {
Integer offset; // Singleton variable
Integer limits; // Singleton variable
List<ObjectsWrapper> objects; // Collection variable since it starts with [],
Integer size; // Singleton variable
}
Since you've array of objects in JSON that's why I've included it as a List in MainWrapper. Now it's time to define ObjectsWrapper. Below is wrapper defined for the same.
public class ObjectsWrapper {
String id;
String modifieddate;
String requestedIds;
PropertyWrapper properties;
}
Since there is only on properties associated with objects that's why it's a non-collection type. Below is representation of properties.
public class PropertyWrapper {
List<NameWrapper> name;
List<String> company;
List<String> title;
List<EmailWrapper> email;
String state;
}
public class NameWrapper {
String name;
String metadata;
}
I guess now you've a fair idea of how to organize data of JSON into various wrapper class. Once you're done with this, simply deserialize the JSON into MainWrapper class and access it.
For example:
MainWrapper mainJSONWrapper = (MainWrapper) JSON.deserialize(JSON,MainWrapper.class);
List<ObjectsWrapper> objectsLst = mainJSONWrapper.objects;
for(ObjectsWrapper obj:objectsLst) {
List<NameWrapper> lstNameWrapper = obj.properties;
for(NameWrapper nameObj:NameWrapper) {
System.debug('Name:'+nameObj.name);
System.debug('metadata:'+nameObj.metadata);
}
}
Above code is not tested but yes, it will give idea how you should deserialize JSON in appropriate manner.
Also go through this answer..How to deserialize a JSON String to Apex

How to use Jackson HyperSchema to generate schema link references

I'm using Jackson jsonSchema(https://github.com/FasterXML/jackson-module-jsonSchema), to automate schema creation for my data model.
According to samples provided in http://json-schema.org/ and in jsonSchema, the format used by json shema includes links
{
"name":"Product",
"properties":{
"id":{
"type":"number",
"description":"Product identifier",
"required":true
},
"name":{
"description":"Name of the product",
"type":"string",
"required":true
},
"price":{
"required":true,
"type": "number",
"minimum":0,
"required":true
},
"tags":{
"type":"array",
"items":{
"type":"string"
}
}
},
"links":[
{
"rel":"full",
"href":"{id}"
},
{
"rel":"comments",
"href":"comments/?id={id}"
}
]
}
But I can't find a way to add it to the generated, schema, although there is a HyperSchema object, which seems to be what I need, but I can't find how to use it.
Made an issue in json-schema project, and a pull request to support HyperSchema, based on annotations, in a form
https://github.com/FasterXML/jackson-module-jsonSchema/issues/35
public class Pet {
public String genus;
}
#JsonHyperSchema(pathStart = "/persons/", links = {
#Link(href = "{name}", rel = "self"),
#Link(href = "{name}/pet", rel = "pet", targetSchema = Pet.class)
})
public class Person {
public String name;
public String hat;
}
Changes are in https://github.com/clemble/jackson-module-jsonSchema