I am currently accepting the student list in Request Body.
public void saveStudents(#RequestBody List<Student> Students){
}
which is accepting below JSON
[
{
"name": "",
"rollNo": ""
},
{
"name": "",
"rollNo": ""
}
]
Instead of the above, I want to accept
{
"students": [
{
"name": "",
"rollNo": ""
},
{
"name": "",
"rollNo": ""
}
]
}
I have tried
public void saveStudents(#RequestBody #JsonProperty("students") List<Student> Students){
}
But it is not accepting.
I do not want to create another object which contains the student list.
Is there any way to assign name to array/list?
#RequestBody annotation stands for sent content - means JSON. There exists no additional mapping methods for scrambling those content inside the annotion, so body represents always an object which can be mapped.
jackson annotation #JsonProperty is defined to annotate fields/methods inside your JSON class - so has no effect at your controller method/parameters
Suggested way
You should IMHO simply accept that you need to create a new class, because it is the easiest and also the correct way to do: For example
import java.util.ArrayList;
import java.util.List;
public class StudentList {
// simplest example approach by public field...
public List<Student> students = new ArrayList<>();
}
And your controller code should look like:
#RequestMapping(path="/api/students",method = RequestMethod.POST)
public void saveStudents(#RequestBody StudentList list) {
// do stuff...
}
So the JSON in body represents a clazz. It's easy to maintain, easy to read, extendable, KISS and also uses spring/jackson defaults without any special configuration.
Other, not suggested ways
I thought about other options to handle this, but none is KISS or good maintainable:
Converter
You could write a converter class, see https://www.baeldung.com/spring-httpmessageconverter-rest
But I think this is an overkill / over engineering to solve avoiding a new value class.
Parse JSON (extreme ugly)
You could handle JSON parsing directly
#RequestMapping(path="/api/students",method = RequestMethod.POST)
public void saveStudents(#RequestBody String json) {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = Json.node(json);
JsonNode students = node.get("students");
// ... than convert back to a list containing students ...
}
But this would be ... terrible to maintain, not KISS , not readable .
You can use:
saveStudents(#RequestParam(value="students") List<Student> students)
saveStudents(#RequestBody Map<String,List<Student>> body)
Hope that could help you.
Related
I use interface-based projections using named-native-query. My UserDTO looks like this:
public interface UserDTO {
#Value("#{target.USER_ID}")
String getId();
#Value("#{target.USER_NAME}")
String getName();
#Value("#{target.REGISTRATION_REGION}")
String getRegistrationRegion();
}
After that I marshall the list of DTOs to Json, and field names which I see there are in camel-case:
{"USERS": [
{
"id": "e00000099232200",
"name": 1616674065,
"registrationRegion": 1617344002
}]}
But I need them in DB style - Upper-case and with underscores like:
{"USERS": [
{
"ID": "e00000099232200",
"NAME": 1616674065,
"REGISTRATION_REGION": 1617344002
}]}
The straightforward way is naming my DTOs methods like getNAME or getREGISTRATION_REGION or iterating over Json fields and make them upper-case there. But is there a more elegant way to set the display name? Something like this:
#Display("REGISTRATION_REGION")
String getRegistrationRegion();
If you're using Jackson you can annotate your interface with:
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
Or if you want this behaviour globally across all usages of your mapper, configure it as follows:
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.UpperCamelCaseStrategy());
EDIT:
You can implement your own CaseStrategy, for your case it will be:
class UpperSnakeCase extends PropertyNamingStrategy.SnakeCaseStrategy{
#Override
public String translate(String input) {
String snakeCased = super.translate(input);
if (snakeCased == null) {
return null;
}
return snakeCased.toUpperCase();
}
}
I am creating a client for the following format of JSON -
{
"results": [
{
"Product": "K265113",
"Language": "EN",
"LongText": "FIXTURE,INTERIOR,WALL"
}
]
}
The JSON always contains "results" field which is an array of a single element (it will always be a single element in this array). I just need LongText field from the JSON and nothing else. I am using Spring RESTTemplate.
I know that it works if I create two DTOs like -
public class ParentDTO
{
private List<ChildDTO> results;
public List<ChildDTO> getResults()
{
return results;
}
public void setResults(List<ChildDTO> results)
{
this.results = results;
}
}
public class ChildDTO
{
private String longText;
public String getLongText()
{
return longText;
}
#JsonProperty("LongText")
public void setLongText(String longText)
{
this.longText = longText;
}
}
But is there any way to read longText by creating a single DTO as the parent DTO is not having any useful field as I know there will always but just one element in the results array.
The reason you need only single DTO could be that you want only single class to perform this task. You can achieve that using ChildDTO as inner class which will make it more readable and maintainable.
The other way is to not parse the spring template response into DTOs instead use JSONNode class of Jackson databind API.
JsonNode root = objectMapper.readTree(response.getBody());
You can find more information at
https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/JsonNode.html
You can traverse down the tree and could retrieve the value of the attribute directly without any DTOs.
Need to map multiple types of JSON responses to a single POJO so that I can compare the different objects to provide insight about the differences.
I had tried mapping the first response to the POJO and parsed the second response to populate the defined POJO:
class XXX {
#JsonProperty("accountHolder")
private String accountHolder;
#JsonProperty("routingNumber")
private String routingNumber;
#JsonProperty("balance")
private List<Balance> balance;
#JsonProperty("accountName")
private String accountName;
#JsonProperty("bankTransferCodeType")
private String bankTransferCodeType;
#JsonProperty("individualInformation")
private IndividualInformation individualInformation;
#JsonProperty("acctType")
private String acctType;
#JsonProperty("transactionList")
private TransactionList transactionList;
#JsonProperty("accountNumber")
private String accountNumber;
#JsonProperty("uniqueId")
private String uniqueId;
#JsonProperty("bankNetID")
private String bankNetID;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
}
First response:
[
{
"ACCOUNT_NAME": "",
"ACCOUNT_NUMBER": "",
"AVAILABLE_BALANCE": null,
"CURRENT_BALANCE": "",
"FULL_ACCOUNT_NUMBER": null,
}
]
Second response:
"bankAccount": [
{
"accountName": "",
"accountNumber": "",
"routingNumber": "",
"fullAccountNumber": "",
"bankTransferCodeType": "",
"acctType": "",
"transactionList": {
"transaction": [
{
"amount": {
"curCode": "",
"content": ""
}
],
"oldestTxnDate": ""
},
"uniqueId":
}
}
Expecting a generic way to map the different structured JSON entities to single POJO.
How to map multiple JSON responses to a single Java POJO?
As both responses seem to be completely different from each other, with nothing in common, I would refrain from attempting to use a single class for reading both responses.
Expecting a generic way to map the different structured JSONs to single POJO.
You could parse both responses as a Map<String, Object> and then map the values to a common class.
You could create separated classes for mapping each response. It will allow you to decouple them and evolve them as you need. You also can use use mapping frameworks such as MapStruct for reducing the boilerplate code when mapping from one object to another.
It doesn’t seems to have any generic way. But you can do this:
Create multiple domain classes for each response type
Create a single standard domain class
Create mapper for each response class to map that to standard domain
class. You can use MapStruct reference here
I would suggest using Jackson Json Views. Here is an example for the same :
Example
public class Views {
public class Global {
}
public class Internal extends Global {
}
}
class XXX {
#JsonView(Views.Global.class)
#JsonProperty("accountHolder")
private String accountHolder;
#JsonView(Views.Internal.class)
#JsonProperty("routingNumber")
private String routingNumber;
}
Hope it helps.
What I did is I created a MyResponse model containing basically all response fields from the JSON response you expect to get.
MyResponse has c-tor or receiving these fields or setters allowing setting them.
Then I created some kind of service class MyService that can issue multiple requests and gets responses back.
Then you just do something like this in some kind of manager class or whatever you call it:
MyService mySer = new MyService();
MyResponse myRes = new MyResponse(
mySer.getDetails(),
mySer.getPicture(),
mySer.getSomethingElse()
);
These calls (getDetails, getPicture...) send requests to end point and return responses which are then just mapped into the the fields of MyResponse class constructor. This happens by the framework so MyResponse has annotations #XmlRootElement and #XmlAccessorType of type FIELD to ensure that happens.
If for whatever reason, you dont want to create response containing result of getPicture for example, you just assign null to that imput parameter.
I suggest to use #JsonProperty("") and #JsonAlias("").
class XXX {
#JsonAlias("accountName")
#JsonProperty("ACCOUNT_NAME")
private String name;
#JsonAlias("routingNumber")
#JsonProperty("ROUTING_NUMBER")
private String routing;}
I hope it helps.
In post service, I am using below method to parse and update Database:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(<String>);
UserLogin userLogin = mapper.convertValue(node.get("UserLogin"), UserLogin.class);
UserSecurityDetails userSecurityDetails = mapper.convertValue(node.get("UserSecurityDetails"), UserSecurity
Now, In get service, I want to send the same data by retieving from DB and adding to JSON. Could anyone suggest what is the best way?
Sample JSON to be formed:
{
"UserLogin":
{
"user_login_id": "10011",
"user_password": "password"
},
"UserSecurityDetails":
{
"user_sequence_id": "1",
"seq_question_id": "1",
"seq_answer": "Test Answer"
}
}
Create a Wrapper POJO having UserLogin and UserSecurityDetails. Jackson will automatically deserialize to your object.
It will be good practice to expect required Object instead of creating objects from String.
Your Wrapper class will be like
public class SecurityDetailsWrapper {
private UserLogin;
private UserSecurityDetails;
// costructor
// getters and setters
}
in your Controller's method you can expect SecurityDetailsWrapper.
like
public void someFunction(#RequestBody SecurityDetailsWrapper wrapper) {
// business logic
}
Jackson will takes care of Deserialization.
This is my JSON from URL
https://api.myjson.com/bins/142jr
[
{
"serviceNo":"SR0000000001",
"serDate":"17",
"serMonth":"DEC",
"serYear":"2015",
"serTime":"02.30 AM",
"serApartmentName":"Galaxy Apartments"
},
{
"serviceNo":"SR0000000002",
"serDate":"19",
"serMonth":"JUN",
"serYear":"2016",
"serTime":"03.30 AM",
"serApartmentName":"The Great Apartments"
}
]
I have one ListView I want populate details from online JSON,above i given a link and sample json anybody given sample jackson code in java
Thanks for advance,
Rajesh Rajendiran
To use jackson you need to create a model class:
[
{
"serviceNo":"SR0000000001",
"serDate":"17",
"serMonth":"DEC",
"serYear":"2015",
"serTime":"02.30 AM",
"serApartmentName":"Galaxy Apartments"
},
{
"serviceNo":"SR0000000002",
"serDate":"19",
"serMonth":"JUN",
"serYear":"2016",
"serTime":"03.30 AM",
"serApartmentName":"The Great Apartments"
}
]
For the above the json the model class would be:
public class SomeClass {
private String serviceNo;
private String serDate;
private String serMonth;
private String serYear;
private String serTime;
private String serApartmentName;
#JsonProperty("serviceNo") //to bind it to serviceNo attribute of the json string
public String getServiceNo() {
return serviceNo;
}
public void setServiceNo(String sNo) { //#JsonProperty need not be specified again
serviceNo = sNo;
}
//create getter setters like above for all the properties.
//if you want to avoid a key-value from getting parsed use #JsonIgnore annotation
}
Now whenever you have the above json as string stored in a variable say jsonString use the following code to parse it:
ObjectMapper mapper = new ObjectMapper(); // create once, reuse
ArrayList<SomeClass> results = mapper.readValue(jsonString,
new TypeReference<ArrayList<ResultValue>>() { } );
results should now contain two SomeClass objects having the above json parsed as respective objects.
PS: Its been a long time since I have used Jackson for parsing so this code might need some improvements.
If you are getting this as http response then I would suggest to use spring rest template for android.
It has support for Message Converters. That way the onus of marshaling and unmarshalling.
[Update]
Here is a blog for the same :http://www.journaldev.com/2552/spring-restful-web-service-example-with-json-jackson-and-client-program
Refer Docs for more details:
http://docs.spring.io/spring-android/docs/current/reference/html/rest-template.html