I am fairly new to using JAX-RS and Jersey. The problem I am facing is that I cannot get a response in JSON the way I would like. I want a response that is something similar to the following:
{
"result": "success",
"car": {
"id": 42,
"name": "toyota",
"model": "camry"
}
}
So far I have a class named Car. It has 3 fields: id, name, and model with getters and setters. I get properly formatted JSON response of a single instance of a Car using the function whose return type is Car.
public Car getCarWithId(#PathParam("id") int id) {
return carService.getCarWithId(id);
}
This only gives me the following:
{
"id": 42,
"name": "toyota",
"model": "camry"
}
How can I get the response with the "result":"success". This will help me solve more complex problems in the future. Thanks for any help.
P.S. I thought about trying to return a hashmap but that gives me an error and seems like I would go into a deeper hole.
Assuming you're provider is Jackson, you could wrap it in a custom ResponseEntity object with a result field, along with a Map<String, Object> with the getter annotated with #JsonAnyGetter. For instance
public class ResponseEntity {
private String result;
private final Map<String, Object> otherFields = new HashMap<>();
public String getResult() {
return result;
}
#JsonAnyGetter
public Map<String, Object> getOtherFields() {
return otherFields;
}
public void addField(String field, Object value) {
otherFields.put(field, value);
}
}
What the #JsonAnyGetter does is make it so that the otherField is not serialized, but only the properties in the Map. You after you create the instance, and all entity.addField("car", carObject);, you will get your desired result.
Related
I am using a DynamoDB-table with DynamoDBAttributeType.M maps nested in one another. The field of my table I'm having problems with looks like this:
"Data": {
"EnglishName": "Balcony",
"High": {
"Status": true,
"Triggered": true,
"Value": 5
},
"Low": {
"Status": true,
"Triggered": false,
"Value": 1
},
"TagName": "tag1"
}
all the keys of the map are Strings and some of the values are Strings and some are maps. In my Java code that data is represented by a class, which is an attribute of another class, which represents whole table. Attributes 'Low' and 'High' are also represented by Java class and attributes of data class.
I have tried to map it to Java object using many ways, mostly by DynamoDBTypeConverter. Hardest part is that I can't find any information about this with Google. I only found one example of converter class where attribute type is S not M.
I keep on getting error:
could not unconvert attribute
How this can be done?
Finally I have a functioning solution. Basically the answer is to use Map<String, Attributevalue> and desired object type in DynamoDBTypeConverter like this:
public class DataConverter implements DynamoDBTypeConverter<Map<String, AttributeValue>, Data> {
#Override
public Map<String, AttributeValue> convert(Data data) {
ObjectMapper mapper = new ObjectMapper();
Item item = new Item()
.withString("englishName", data.getEnglishName())
.withMap("high", mapper.convertValue(data.getHigh(), Map.class))
.withMap("low", mapper.convertValue(data.getLow(), Map.class))
.withString("tagName", data.getTagName());
return ItemUtils.toAttributeValues(item);
}
#Override
public Data unconvert(Map<String, AttributeValue> data) {
Data tagData = new Data();
try {
ObjectMapper mapper = new ObjectMapper();
String item = ItemUtils.toItem(data).toJSON();
JsonNode json = mapper.readTree(item);
tagData = mapper.convertValue(json, Data.class);
} catch (JsonProcessingException ex) {
Logger.getLogger(TagDataConverter.class.getName()).log(Level.SEVERE, null, ex);
}
return tagData;
}
I also needed to write type converters to attributes that are objects and annotate them in class:
public class Data {
private String tagName;
private String englishName;
#DynamoDBTypeConverted(converter = AlertConverter.class)
private Alert low;
#DynamoDBTypeConverted(converter = AlertConverter.class)
private Alert high;
The AlertConverter.class is an ordinary DynamoDBTypeConverter that takes in <String, Alert>.
Have you tried using a tool like Gson? If you're getting back the DynamoDB response as a JSON string, try this:
Gson gson = new Gson();
Data data = gson.fromJson(jsonString, Data.class);
I am trying to figure out how to return multiple JSON items. Right now I am able, to return a single JSON like so:
{
"result": {
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
"error": null
}
But I would like to return multiple JSON items, like so:
{
"result": {{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
}
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
},
"error": null
}
I can view the multiple JSON items as string, like below, but I would like to return it as multiple JSON items, but I don't know how:
[LDAPModel(userId=abcde123, telephoneNumber=1-555-5555555), LDAPModel(userId=fghi456, telephoneNumber=1-333-3333333]
I am a complete beginner in Java, and I don't know the syntax or much in Java. But I was given these codes (including the one below) from SpringBoot; I really don't understand what it is doing, and so I have no idea how create an output of list.
Currently, this is what I was given:
public Optional<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
// for (int ii = 0; ii < ldapModelList.size(); ii++) {
// Optional.of(ldapModelList.get(ii));
// ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(ii));
// }
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList.get(0));
}
I tried putting it in a loop (like in the commented out code above), but I don't know how create a list. I tried removing the get(0), but there was a syntax error... There are many things I tried, but it just did not help.
Anyone can help?
Update/Edit: Thank you all for your answers. I posted a follow up question here. If you have a chance, please help me out. Thanks.
First of all I would like to point out that your JSON isn't formatted properly. When you want to represent multiple objects in JSON you should use square brackets and separate each object with a comma:
{
"result": [
{
"userId": "abcde123",
"telephoneNumber": "1-555-5555555"
},
{
"userId": "fghi456",
"telephoneNumber": "1-333-3333333"
}
],
"error": null
}
The reason your Java code does not work when you try and remove get(0) is because the method public Optional<LDAPModel> getDirectReports(String cdsID) returns an Optional<LDAPModel> type and by removing get(0) your are effectively trying to return an Optional<List<LDAPModel>>. If you want the method to return a list instead of a single object you can change the return type to Optional<List<LDAPModel>> and then safely remove get(0).
public Optional<List<LDAPModel>> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList.isEmpty() ? Optional.empty() : Optional.of(ldapModelList);
}
The structure looks strange to me. What you have looks like you want result to be an array of objects:
{
"result": [
{ "userId": "abcde123",
"telephoneNumber": "1-555-5555555" }
{ "userId": "fghi456",
"telephoneNumber": "1-333-3333333" }
],
"error": null
}
Given a reasonable JSON library, then the value of the "result" member of the JSON object is a JSON array, from which you can then pick out each element in turn by indexing, and each element is a JSON object with 2 members.
I assume you already managed to get all the list/array of LDAPModel i.e. List ldapModelList
If so, you just need to return this ldapModelList in your getDirectReports method.
public List<LDAPModel> getDirectReports(String cdsID) {
LdapQuery ldapQuery = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.where("objectclass").is("person")
.and("managerID").like(cdsID);
List<LDAPModel> ldapModelList = ldapTemplate.search(ldapQuery, (Attributes attrs) ->
LDAPModel.builder()
.userId(getValue(attrs, "userid"))
.telephoneNumber(getValue(attrs, "phoneNumber"))
.build());
return ldapModelList;
}
Then just use your library to return the json array. I suppose you use jackson.
Just make sure in LDAPModel you have
getters and setters
empty constructor if you add your own constructor having params. But if you don't add any constructor, then no need to add this default empty constructor as java will automatically create it for you.
LDAPModel class is as follows:
public class LDAPModel {
String userId;
String telephoneNumber;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setTelephoneNumber(String telephoneNumber) {
this.telephoneNumber = telephoneNumber;
}
}
For the object to JSON string conversion using Jackson, I assume you already know it or can find out how.
I have to send a batch of instructions in JSON data format as below:
{
"batchId": "123456",
"instructions": [ {
"instructionId": "1",
"customer": {
"iban": "abc",
"name": "abc"
}
},
"instructionId": "2",
"customer": {
"iban": "abc",
"name": "abc"
}
}
.
.
.
.. . . ..
]
}
Now, i will fetch multiple records/instructions from the database via query, which I will use to generate the JSON data-set as per the format above.
What I have researched: I have traversed many solutions and thought of my own that I shall create an object class of Instruction mapping with setters and getters. I shall use a loop through each record and set values of object fields from database via setter and add entry that complete object entry to JSON via getters.
I will continue this approach until the records are read completely.
My question is: Is this solution efficient and best one to deal with such requirement or any other suggestion?
Please suggest? Any sort of code snippet or help is appreciated.
You can use com.google.gson for that.
Create the Instruction object and then add the fields and annotate the field with SerializedName if you are mapping a different json name to your java field(E.g. instruction_id to instructionId).
import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.Map;
public class Instruction {
#SerializedName("instructionId")
private String instructionId;
#SerializedName("customer")
private List<Map<String, Object>> customer;
public String getInstructionId() {
return instructionId;
}
public void setInstructionId(String instructionId) {
this.instructionId = instructionId;
}
public List<Map<String, Object>> getCustomer() {
return customer;
}
public void setCustomer(List<Map<String, Object>> customer) {
this.customer = customer;
}
}
And then you can map your json to your instruction class.
Instruction instruction = new GsonBuilder()
.setPrettyPrinting()
.create()
.fromJson(json, Instruction.class);
I have a baseclass and three extending classes. For example:
BaseClass:
public BaseClass {
int id;
}
public SubClass extends BaseClass {
int sub1;
}
public SubClass2 extends BaseClass {
int sub2;
}
Now i want to send a json file to my spring server and the server must check if it is a SubClass-type or a SubClass2-type
#PostMapping(value = "/test", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> create(#RequestBody List<BaseClass> entry);
sending JSON:
{
"id": 1,
"sub1": 1
},
{
"id": 2,
"sub2": 2
}
I except a List of BaseClasses but try to cast them in the specific subclass. How can i do this? Following did not work.
if (abc instanceof SubClass) {
log.info("abc is instance of SubClass");
} else if (abc instanceof SubClass2) {
log.info("abc is instance of SubClass2");
}
If you will accept BaseClass Spring will map your json to be compliant with only BaseClass ignoring all the other fields. So your check for subclasses will not work at all.
The simplest solution is to accept data as plain text and after that manually try to map it to your models using GSON or something similar like this
new Gson().fromJson("{
"id": 1,
"sub1": 1
}", SubClass.class);
But it is a bad way to handle this.
Your approach need to be changed architecturally. The simplest way to do it is to introduce data field to you model like this
{
"id": 1,
"data": {
"key": "sub1",
"value": 1
},
},
public Data {
String key;
int value
}
public BaseClass {
int id;
Data data;
}
this way you will be able to check
if (abc.data.key.equals("sub1")) {
log.info("abc is sub1");
} else if (abc.data.key.equals("sub1")) {
log.info("abc is sub2");
}
This will require a bit more complex further implementation, but it will work.
Hope it helps.
{
"localeCode": "",
"map": {
"DynamicName1": [],
"DynamicName2": [
{
"date": "2016-05-15T00:00:00",
"seqId": 1,
"status": 10
},
{
"date": "2016-05-16T00:00:00",
"seqId": 83,
"status": 10
}
],
"DynamicName3": [],
"DynamicName4": []
},
"respCode": 100,
"respMsg": "success",
"status": 1
}
How to correctly map this kind of json. If you can see that, Dynamic is a dynamic name. So far I have done this :
public class MapModel {
public MapObject map;
public static class MapObject{
public java.util.Map<String, Student> queryStudent;
public static class Student{
public String date;
public String seqId;
public String status;
}
}
}
But when run the app. I'm getting NullPointerException. Can somebody help me?
You're getting the NullPointerException accessing queryStudent of your MapObject inside your MapModel since it's not correctly filled when you're trying to deserialize your Json.
So to solve your problem look at Gson documentation where you can see:
You can serialize the collection with Gson without doing anything
specific: toJson(collection) would write out the desired output.
However, deserialization with fromJson(json, Collection.class) will
not work since Gson has no way of knowing how to map the input to the
types. Gson requires that you provide a genericised version of
collection type in fromJson(). So, you have three options:
Use Gson's parser API (low-level streaming parser or the DOM parser
JsonParser) to parse the array elements and then use Gson.fromJson()
on each of the array elements.This is the preferred approach. Here is
an example that demonstrates how to do this.
Register a type adapter for Collection.class that looks at each of the
array members and maps them to appropriate objects. The disadvantage
of this approach is that it will screw up deserialization of other
collection types in Gson.
Register a type adapter for MyCollectionMemberType and use fromJson()
with Collection.
Since your MapObject containts a java.util.Map but your class itself it's not generic, I think that a good approach for your case is create a Deserializer.
Before this try to clean up your class definition, to provide constructors to make the deserializer easy to build. Your POJO classes could be:
Student class
public class Student{
public String date;
public String seqId;
public String status;
public Student(String date, String seqId, String status){
this.date = date;
this.seqId = seqId;
this.status = status;
}
}
MapObject class
Note: I change you Map definition, since in your Json seems that could be multiple students for each DynamicName (look at DynamicName2 from your question), so I use Map<String,List<Student>> instead of Map<String,Student>:
public class MapObject{
public Map<String,List<Student>> queryStudent;
public MapObject(Map<String,List<Student>> value){
this.queryStudent = value;
}
}
MapModel class
public class MapModel {
public MapObject map;
}
Now create a Deserializer for your MapObject:
public class MapObjectDeserializer implements JsonDeserializer<MapObject> {
public MapObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Map<String,List<Student>> queryStudents = new HashMap<String,List<Student>>();
// for each DynamicElement...
for (Map.Entry<String,JsonElement> entry : json.getAsJsonObject().entrySet()) {
List<Student> students = new ArrayList<Student>();
// each dynamicElement has an Array so convert and add an student
// for each array entry
for(JsonElement elem : entry.getValue().getAsJsonArray()){
students.add(new Gson().fromJson(elem,Student.class));
}
// put the dinamic name and student on the map
queryStudents.put(entry.getKey(),students);
}
// finally create the mapObject
return new MapObject(queryStudents);
}
}
Finally register the Deserializer and parse your Json:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MapObject.class, new MapObjectDeserializer());
Gson gson = builder.create();
MapModel object = gson.fromJson(YourJson,MapModel.class);
DISCLAIMER: For fast prototyping I test this using groovy, I try to keep the Java syntax but I can forget something, anyway I think that this can put you on the right direction.
Hope it helps,