I new to Spring boot microservices and exploring webflux framework. I'm trying to merge response from two microservices to one wrapper class to have the response in proper JSON. Below is the scenario in detail.
Micro-Service 1 : http://localhost:8080/products
in controller of this micro-service its returning Flux and I'm get
[
{
"id": "5b2fd1e5f57d731904c54ad7",
"name": "Product3",
"price": "30"
},
{
"id": "5b2fd1e4j9fdj3kds9djkj43",
"name": "Product2",
"price": "20"
}
]
Micro-Service 2 : http://localhost:8181/person
In controller of second service its returning Mono and for this also I'm getting correct response as below,
{
"id": ehj8u3jmodmdj,
"name": "PersonXXX",
"email": "PersonXXX#somecorp.com"
}
Now I want to create another microservice http://localhost:8282/personproduct which should combine the result of above two microservices in a wrapper class as below,
{
{
"id": ehj8u3jmodmdj,
"name": "PersonXXX",
"email": "PersonXXX#somecorp.com"
},
[
{
"id": "5b2fd1e5f57d731904c54ad7",
"name": "Product3",
"price": "30"
},
{
"id": "5b2fd1e4j9fdj3kds9djkj43",
"name": "Product2",
"price": "20"
}
]
}
Right now I have a Parent class Entity for both Product and Person classes and I'm calling both above mentioned micro-services via WebClient and concatinating the response using Flux.concat(personResp, productResp); where personResp is of type Mono and productResp is of type Flux but I'm getting response of this (3rd) microservice only in Text and not in JSON as below,
data:{"id":ehj8u3jmodmdj,"name":"PersonXXX","email":"PersonXXX#somecorp.com"}
data:{"id":"5b2fd1e5f57d731904c54ad7","name":"Product3","price":"30"}
data:{"id":"5b2fd1e4j9fdj3kds9djkj43","name":"Product2","price":"20"}
This might be due to each element is sent as a different stream.
So just want to know if is there any way to combine two responses in one wrapper class without using block() method on any of the calls done to these two services.
UPDATE
Currently Im calling product microservice as,
clientProd.get().uri(productUrl)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().bodyToFlux(Product.class).onErrorReturn(new Product());
And similarly Person service as,
clientPerson.get().uri(personUri)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve().bodyToMono(Person.class).onErrorReturn(new Person());
And concatenating is using Flux.concat(),
Thanks in advance.
The correct way is to map those responses to classes:
public class Product{
private Long id;
private String name;
private Double price;
//constructors, getters, setters
}
public class Person{
private Long id;
private String name;
private String mail;
//constructors, getters, setters
}
public class Entity{
private Person person;
private List <Product> products;
//constructors, getters, setters
}
In this way you have three different POJOs that you can use according to the needs (the type of API you are calling).
Related
I am sending the following request (using Spring Boot)
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
response is the json object(i have ommitted lot of fields in json object)
{
"customer": {
"id": 100,
"ci": {
"lDb": "11",
"localId": "1"
},
"cusdata": {},
"rating": {
"id": 3140,
"dateTime": "2019-09-21 06:45:41.10",
"rawData": {
"seg": "XYZ",
"seg2": "XYZ",
"et": "XYZ_CORP",
"CountryCodes": [
"IN"
],
"doBusiness": "2017-09-20"
],
...
....
...
...
"status": "SUCCESS"
}
I need to map the below fields to a Pojo Class
1.localId
2.seg
3.seg2
4.status
How can i create the PojoClass such that those fields are mapped automatically
So basically how will my PojoClass should look like?
ResponseEntity<PojoClass> response = restTemplate.exchange(url, HttpMethod.GET, request, PojoClass.class);
I suggest that you use sites like http://www.jsonschema2pojo.org/. There, you can select many options on the right panel and adjust POJO you want to get from JSON schema.
Your PojoClass has to follow the structure of the JSON that your are receiving and have the fields that your are interested (or all of them).
For the first level class:
public class PojoClass {
private Customer customer;
private String status;
...
}
Then, create a Customer class for the customer fields and create more classes for the rest of the fields:
public class Customer {
public String id;
public CI ci;
public CustData custData;
...
}
Create a custom class PojoClass
public class PojoClass {
private Integer id;
private Object ci;
private Object cusdata;
private Object rating;
private Object status;
}
ResponseEntity<PojoClass> responseEntity = restTemplate.exchange(url,HttpMethod.GET,request,new ParameterizedTypeReference<PojoClass>(){
});
is there anyway to correct Map method responses which use generics as response subfields with the appropriate implementation? Example:
Let's say I have a generic response class:
public class Response {
private String createdOn;
//other fields
private Map<String, Object> values;
private Model object;
//getters and setters
}
Generic model class:
public class Model{
}
Model Class A:
public class ModelA extends Model{
private String name;
private String lastname;
//getters and setters
}
And Model Class B:
public class ModelB extends Model{
private int number;
private boolean isTrue;
//getters and setters
}
And method doSomeStuff() which let's say returns a ResponseEntity< Response> where in this particular response "values" is a map< String, SomeRandomObject> and object is a ModelA object.
And suppose I have another method doSomeOtherStuff() which returns a ResponseEntity< Response> object where in that case "values" is a map< String, AnotherRandomObject> and object is a ModelB object.
Is there any way to map the response subfields of the first method to the appropriate ones? Using swagger-UI all I get is the following for both methods:
{
"createdOn": "string",
// other fields
"model": {},
"values": {}
}
Whereas I would like something like the following:
doSomeStuff():
{
"createdOn": "string",
// other fields
"model": {
"name": "string",
"lastname": "string",
}
"model": {
//All the subfields of SomeRandomObject
},
}
doSomeOtherStuff():
{
"createdOn": "string",
// other fields
"model": {
"number": "int",
"isTrue": "boolean",
}
"model": {
//All the subfields of AnotherRandomObject
},
}
Since it obviously cannot determine what type of objects are returned at the implementation level.
P.S. The above classes are as an example. I would really appreciate help to make the documentation work and not for ways to refactor it since the above logic is applied to more than 100 rest methods.
Edit: I am using springfox-swagger2 with springfox-swagger-ui.
Lets say we have the following JSON example:
{
"teachers": [{
"id": "abc",
"payment": 10,
"name": "xyz",
"clases": ["1", "3"]
}, {
"id": "qwe",
"payment": 12,
"name": "xcv",
"classes": ["1", "2"]
}],
"classes": [{
"id": "1",
"room": 7
}, {
"id": "2",
"room": 1
}, {
"id": "3",
"room": 2
}]
}
I would like to deserialize it to Java objects (getters/setters ommited):
class Teacher {
private String id;
private double payment;
private String name;
private List<CLassRoom> classRooms;
}
class ClassRoom {
private String id;
private int room;
}
As you see, we have a references here. I know I can deserialize it with Jackson (and would like to) but the problem is that I cannot touch DTO itself (so annotations are not possible, would also like to avoid wrappers (many classes)). Also, it would be nice if the "configuration" of deserialization was in separate file (json schema for example). I would also like to avoid some tags given by user - he should only pass me the values. Moreover, he should know where is the error, if he made some mistake.
Also, it would be nice if I could manipulate name of field in json (some clients may have different habits).
I didn't find anything which satisffied all of above requirements(entity reference and error handling are the most important). However - I just have heard about json schema, so maybe it provides such functionality (but I didn't find it though). Any helpful reference/example/lib? I will appreciate any help.
Just to be correct - imagine that the given json is a RELATIONAL database snapshot of the instance. I just want to create whole entity like the hibernate (or actually JPA) does :)
1. add jar of import org.json.JSONObject.
2. JSONObject object = new JSONObject(list)
2.1 object.has("teachers") if it is exists
2.2 JSONArray teacherArray = (JSONArray) object.get("teachers");
2.3 JSONObject teacherJsonObject = teacherArray .getJSONObject(0);
(if you have more than jsonobject in json arrary then itrate it.)
2.4 if(teacherJsonObject .has("id"))//you can check existence like this.
String id=teacherJsonObject .getString("id");
String payment=teacherJsonObject .getString("payment");
String name=teacherJsonObject .getString("name");
It may not be the best solution, but it's a working one.
Let's create a Parser class like the following:
public class Parser {
private List<Teacher> teachers;
private List<ClassRoom> classes;
public void parse() {
for (Teacher teacher : teachers) {
for (String classRoomId : teacher.getClasses()) {
for (ClassRoom classRoom : classes) {
if (classRoom.getId().equals(classRoomId)) {
teacher.getClassRooms().add(classRoom);
}
}
}
}
}
}
Modify your ClassRoom class to have a getter on the id field:
public class ClassRoom {
private String id;
private int room;
public String getId() {
return id;
}
}
And your Teacher class to get the Ids of classes AND the classRooms references:
public class Teacher {
private String id;
private double payment;
private String name;
private String[] classes;
private List<ClassRoom> classRooms = new ArrayList<>();
public String[] getClasses() {
return classes;
}
public List<ClassRoom> getClassRooms() {
return classRooms;
}
}
If you use the Gson library, you could then just parse your JSON like that:
Gson gson = new Gson();
Parser parser = gson.fromJson(jsonString, Parser.class);
parser.parse;
Now, every teacher will have their classRooms correctly referenced.
How can I save JSON object with different level of hierarchies in MongoDB ?
For example :-JSON
{
"name": "abc",
"password": "xyz",
"address": {
"street": "ghgjk",
"pin": 25646
},
"readingHabbits": [
"jkjsdj",
"sdkhks",
"jlcsd"
],
"eatingHabbits": {
"internalObjOne": {
"dkks": "jdskdfl",
"lfld": "hfslvlsk"
},
"internalObjectSecond": {
"cjdlksl": "hcdkjnjkcs",
"cjsdjljsl": "chsdskjc"
}
}
}
How can I store above JSON values into MongoDB in different Collections using SpringBoot and Java?
Also how can I get the same result when I do repository.findAll();
It should look like this:
db.getCollection("images").insertOne(
new Document("name", "abc")
.append("password", "xyz")
.append("address",
new Document()
.append("street", "ghgjk")
.append("pin", 25646))
.append("readingHabbits", Arrays.asList("jkjsdj", "sdkhks", "jlcsd"))
.append("eatingHabbits", Arrays.asList(
new Document()
.append("internalObjOne",
new Document()
.append("dkks", "jdskdfl")
.append("lfld", "hfslvlsk")),
new Document()
.append("internalObjectSecond",
new Document()
.append("cjdlksl", "hcdkjnjkcs")
.append("cjsdjljsl", "chsdskjc"))
)
)
);
thanks for the answer, But I think I didn't made myself clear.
Suppose I have a json object.
{ "name": "abc", "password": "xyz", "address": { "street": "ghgjk", "pin": 25646 }, "readingHabbits": [ "jkjsdj", "sdkhks", "jlcsd" ], "eatingHabbits": { "internalObjOne": { "dkks": "jdskdfl", "lfld": "hfslvlsk" }, "internalObjectSecond": { "cjdlksl": "hcdkjnjkcs", "cjsdjljsl": "chsdskjc" } } }
Base class :-
#Document(collection="user")
public class User{
#Id
private String id;
private String name;
private String password;
#DBRef
private Address address;
#DBRef
private ReadingHabbits readingHabbits;
//setter and getter
}
#Document(collection="address")
public class Address{
#Id
private String id;
private String street;
private int pin;
//setter and getter
}
Do we need to create separate classes for "ReadingHabbits" and "EatingHabbits" ?
If not then is the above approach correct to map two different entities with DBRef and save it to two different collection in MongoDB?
Or do we have any other way to do the same operation ?
----------
I am making a query call to freebase and I receive a JSON response. The response has the following structure:
{
"code": "/api/status/ok",
"result": [
{
"/common/topic/image": [{
"guid": "#9202a8c04000641f8000000004b67f6d"
}],
"/people/person/profession": [{
"name": "Critic"
}],
"id": "/en/michael_jackson_1942",
"name": "Michael Jackson",
"type": "/people/person"
},
{
"/common/topic/image": [{
"guid": "#9202a8c04000641f800000001b90fdea"
}],
"/people/person/profession": [{
"name": "Actor"
}],
"id": "/en/michael_jackson_1970",
"name": "Michael Jackson",
"type": "/people/person"
}
],
"status": "200 OK",
"transaction_id": "cache;cache03.p01.sjc1:8101;2012-01-16T18:28:36Z;0055"
}
I need to parse this response in a ArrayList of java objects using GSON. To do this I need to create the class of the object with get/set and make it available to parse. Or is there another simpler way to do things ? I have used simple JSON strings by now, but in this case I can't remake the structure of the class I need. Basically in the end I need something like ArrayList<Person> where Person has all the attributes from the json string.
Any help is appreciated. Thank you.
The final solution, according with the answer below
public class FreebaseResponse {
#SerializedName("code")
public String code;
#SerializedName("result")
public ArrayList<Person> result;
#SerializedName("status")
public String status;
#SerializedName("transaction_id")
public String transaction_id;
}
public class Person {
#SerializedName("/common/topic/image")
public ArrayList<Person.Guid> imageGuid;
#SerializedName("/people/person/profession")
public ArrayList<Person.Profession> profession;
#SerializedName("id")
public String id;
#SerializedName("name")
public String name;
#SerializedName("type")
public String type;
private class Guid
{
#SerializedName("guid")
public String guid;
}
private class Profession
{
#SerializedName("name")
public String name;
}
}
I guess you can create a FreebaseResponse class that contains code, result (ArrayList<Person>), etc fields and use Gson to deserialize. the names that are not valid identifiers, e.g. /common/topic/image will be a problem. I haven't tried it myself but it seems to me that SerializedName annotation should do the trick.
If you need all the fields, the way you mentioned seems to me like the way to go. If you only want a small part of the data, you can get it directly. In general, I think that since JSON is meant for object representation, it is better to create the appropriate class. It will ease your way in the future as well, as you will need more from this data.