How to modify and bind response to DTO - java

I am very new to spring boot development.
Currently, I am using #FeignClient to call external API and I want to bind it to my DTO.
My DTO looks like
public class TestDTO {
private UUID uuid;
private String name;
}
My #FeignClient,
#FeignClient(name = "testClient", url = "http://extenalApi/getRules")
public interface DataClient {
#RequestMapping(method = RequestMethod.GET)
List<TestDTO> getRules();
}
It throws an error because external API response is a bit different
{
"data": [
{
"customRule": {
"name": "SAMPLE 1",
"id": "0AB58A47D3A64B56A6B74DA0E66935DD"
}
},
{
"customRule": {
"name": "SAMPLE 2",
"id": "0AE6C846EAF648D1926E665E63633A94"
}
}
}
how can I parse this JSON and make it to
[
{
"name": "SAMPLE 2",
"id": "0AE6C846EAF648D1926E665E63633A94"
},
{ ...
}
]
as my DTO demands.

Seems like you have different structure of API reponse.
Create a new container to hold API response of rules api:
public class RulesDTO {
private List<DataDto> data;
//getter setter
class DataDto{
TestDTO customRule;
//getter setter
}
}
change method to getRules:
#RequestMapping(method = RequestMethod.GET)
RulesDTO getRules();
Now parse it to List of TestDTO for your response:
List<TestDTO> yourData = data.stream().map(DataDto::getCustomRule).collect(Collectors.toList());
Note- It is not working code. Just to give you an idea.

Related

Is it Possible to send a Java Object to C# App using Kafka

Is it possible to send send a Java Object (lets say user) to a topic that is consumed and serialised to a User Object in C#?
Lets say I have the following avro schema built from a a Java PoJo (Fields are Name and Age)
{
"namespace": "io.confluent.developer",
"type": "record",
"name": "User",
"fields": [
{
"name": "name",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "age",
"type": [
"null",
"int"
],
"default": null
}
]
}
Which generates a User.class
Then sending it like so:
Service
#CommonsLog(topic = "Producer Logger")
#RequiredArgsConstructor
public class Producer {
#Value("${topic.name}")
private String TOPIC;
private final KafkaTemplate<String, User> kafkaTemplate;
void sendMessage(User user) {
this.kafkaTemplate.send(this.TOPIC, user.getName(), user);
log.info(String.format("Produced user -> %s", user));
}
}
I also have a Schema registry BUT I do not know how to consume the message in C# and deserialise it to a User class with the same fields:
public class Users
{
public int id = 0;
public string name = string.Empty;
public Users()
{
// Constructor Statements
}
public void GetUserDetails(int uid, string uname)
{
id = uid;
uname = name;
Console.WriteLine("Id: {0}, Name: {1}", id, name);
}
public int Designation { get; set; }
public string Location { get; set; }
}
Thanks for the help.
Yes it's possible. You can use official .NET kafka client to consume messages.
First thing you have to do is generate C# class based on the same schema you used. You can do that by:
Installing avrogen tool:
dotnet tool install --global Apache.Avro.Tools
Generate the class: avrogen -s user_schema.avsc .
Then you will get User.cs with the class implementation. All you need to do is configure .NET Kafka client and consume the messages:
var schemaRegistryConfig = new SchemaRegistryConfig
{
Url = "schemaRegistryUrl"
};
var consumerConfig = new ConsumerConfig
{
BootstrapServers = "bootstrapServers",
GroupId = "group"
};
using var schemaRegistry = new CachedSchemaRegistryClient(schemaRegistryConfig);
using var consumer = new ConsumerBuilder<string, User>(consumerConfig)
.SetValueDeserializer(new AvroDeserializer<User>(schemaRegistry).AsSyncOverAsync())
.Build();
consumer.Subscribe(topicName);
var consumeResult = consumer.Consume(cts.Token);
You can have a look at this example for more info.
Notice, that you can't use the User class you provided in your question, because there are some requirements for the class structure. So you should use the one generated with the tool from your Avro schema.

Swagger/OpenAPI 3.0 generation - adding a new content-type hides previous schema information

I am using Java annotations to build our swagger documentation. In one case, we want to provide output in either JSON or csv depending on the "Accepts" header. I created the rest interface like this:
#RestController
#EnableAutoConfiguration
#RequestMapping(path="/api/v2/swaggerTest")
#Api
public class SwaggerDocResource {
private static class ItemDto {
String name;
Integer age;
}
#ApiOperation(value = "Get the requested items in json")
#GetMapping(produces = "application/json")
public ResponseEntity<ItemDto> getItems() {
return null;
}
#ApiOperation(value = "Get the requested items in csv")
#GetMapping(produces = "text/csv")
public ResponseEntity<String> exportItems() {
return null;
}
}
If I comment out the csv method, the generated Swagger doc generates a schema for my DTO class and references it:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ItemDto"
}
}
}
},
...
However, if I do include the csv method, the schema for my DTO class is no longer generated and both types of response are given the same schema:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "string"
}
},
"text/csv": {
"schema": {
"type": "string"
}
}
}
},
...
Is it possible to assign a different schema to these different content types? I have been unable to figure out how.

Jackson deserialization bellow JSON property

I want to fetchMultiple(ParameterizedTypeReference<List<T>> responseType) for a given List<T>, in this case, I want to get directly a List<Account> but I am getting an error because the list of accounts is encapsulated in another object, as shown below:
{
"accounts": [
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
}
There is some Jackson annotation to filter this somehow in order to be processed like this:
[
{
"accountUid": "c75deb59-5d52-4a23-af7b-fce29927ce9d",
"defaultCategory": "b4189da5-7688-42d0-86e3-14ae9031e01d",
"currency": "GBP",
"createdAt": "2020-08-05T16:50:50.536Z"
}
]
POJO
#Data
public class Account {
private String accountUid;
private String defaultCategory;
private String currency;
private String createdAt;
}
RestRequestTemplate.java
public List<T> fetchMultiple(ParameterizedTypeReference<List<T>> responseType) {
return new RestTemplate().exchange(this.url, this.httpMethod, this.request, responseType).getBody();
}
AccountsServiceImpl.java
public List<Account> getAccounts() {
RestRequestTemplate restRequestTemplate = new RestRequestTemplate(GET_ACCOUNTS, HttpMethod.GET, Collections.EMPTY_MAP);
return restRequestTemplate.fetchMultiple(new ParameterizedTypeReference<List<Account>>() {});
}
There is indeed an annotation to ignore the root object. It is called #JsonUnwrapped. Annotate your method with that annotation and your json should be without the root object.

How to achieve my JSON format to be in certain format?

I am expecting my JSON response to be in a certain format.
Here is my Company, LocationInfo looks like
public class Company {
private List<LocationInfo> companies;
// gettts and setters
}
public class LocationInfo {
private String localCompanyID;
private String localCompanyName;
// gettts and setters
}
My controller looks like below
#GetMapping(value = "/url", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<LocationInfo> getLocationMaster() {
ResponseEntity response;
List<LocationInfo> companies = getAllCompanies();
// some code goes here
response = new ResponseEntity<>(localCompanies, HttpStatus.OK);
return response;
}
This is what I'm getting
[
{
"localCompanyID": "33",
"localCompanyName": "Company-A"
},
{
"localCompanyID": "45",
"localCompanyName": "Company-B"
}
]
I'm expecting response to be as below.
{
"localCompanies": [
{
"localCompanyID": "33",
"localCompanyName": "Company-A"
},
{
"localCompanyID": "45",
"localCompanyName": "Company-B"
}
]
}
Thanks in advance.
Create a response DTO class and add your list to it:
#AllArgsConstructor(staticName = "of")
class LocalCompaniesResponseDto {
List<LocationInfo> localCompanies;
}
new ResponseEntity<>(LocalCompaniesResponseDto.of(localCompanies), HttpStatus.OK)

How to manipulate nested Json Data to de-Nest in java

I have been looking solution for this problem but could not find one so asking this question.
I have some data which looks like this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation": {
"firstName": "jio",
"lastName": "g",
"mobileNumber": "1234567890",
},
"accountBalance": 0,
}
]
},
I want to write a java code to change the data structure to this
{
"data": [
{
"id": "5ab892c71810e201e81b9d39",
"isSignedUpUsingFb": false,
"personalInformation_firstName":"jio",
"personalInformation_lastNAme":"g",
"personalInformation_mobileNumber":"1234567890",
"accountBalance": 0,
}
]
},
I am getting data from db as:
#Override
public List<User> getAllUsers() {
logger.debug("entering all users method");
List<User> allUsers=mongoOperations.findAll(User.class);
for (User user : allUsers) {
PersonalInformation info=user.getPersonalInformation());
//manipulation code here
user.setPersonalInformation(info);
}
return allUsers;
}
So I want to write a logic so that i can convert the data in desired format and send it a return type. I know how to do same thing using J query but I want to do it in backend so any code for the above or any link will help.
I have fond one solution which is very simple.So, basically when we create object for nested data we create it like this in JAVA.
public MyClass{
public String name;
public String contact;
public PersonalInformation personalinformation;
//setters and getter here
}
this will give me data as
"MyClass":{
"name": "abc",
"contact": "12345",
"personalInformation":{
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
}
so to remove this nested data we need to use #JsonUnwrapped which removes all the nested object and add it to our main object.
public MyClass{
public String name;
public String contact;
#JsonUnwrapped
public PersonalInformation personalinformation;
//setters and getter here
}
which will change the data structure as:
"MyClass":{
"name": "abc",
"contact": "12345",
"address": "asdasdasdad",
"city":"asdadad",
"pin": "asdfg",
}
for more reference you can check this link http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
Hope this helps.
There are multiple possible solutions. As Prabhav has mentioned the most intuitive one would be to create a new class and from there a object which can be transformed with a library to a JSON.
Variant one:
The new class would look like your data structure you want and access would be:
PersonalInformationJson pf = new PersonalInformationJson();
pf.setFirstName = info.getPersonalInformation_firstName
//... setting the rest of the object
//using jackson
ObjectMapper mapper = new ObjectMapper();
try {
// convert user object to json string and return it
String jsonString = mapper.writeValueAsString(u);
}
The other easier version to create a string, either per hand or use a lib:
// using org.json.JSONObject
String jsonString = new JSONObject().put("personalInformation_firstName", info.value())
.put("personalInformation_lastNAme", info.value());

Categories

Resources