Request:
{
"name":"iswarya",
"dept":{
"deptName":"eee",
"location":"firstfloor"
},
"additionalDetails":{
"projectName":"finalyearproject"
}
}
Response:
{
"name": "iswarya",
"deptName": null,
"location": null,
"projectName": null
}
Controller class:
#PostMapping(value="/objectMApper")
public String createEmployee(#RequestBody AnnotationTestBean demoEntity) throws JsonProcessingException {
ObjectMapper obj=new ObjectMapper();
return obj.writeValueAsString(demoEntity);
}
In the given example the request for JSON is not wrapped, so its dept and additionalDetails should not be annotated with #JsonUnwrapped.
Instead, a response should be created extending the request class, having a copy constructor, and overriding appropriate getters annotated as #JsonUnwrapped.
The example below uses Lombok annotations to generate getters/setters/constructors.
#Data
#AllArgsConstructor
#NoArgsConstructor
static class Request {
private String name;
private Department dept;
private Details additionalDetails;
}
#Data
static class Department {
private String deptName;
private String location;
}
#Data
static class Details {
private String projectName;
}
static class Response extends Request {
public Response(Request request) {
super(request.name, request.dept, request.additionalDetails);
}
#Override #JsonUnwrapped
public Department getDept() { return super.getDept(); }
#Override #JsonUnwrapped
public Details getAdditionalDetails() { return super.getAdditionalDetails(); }
}
Test
ObjectMapper om = new ObjectMapper();
String json = "{\r\n" +
" \"name\":\"iswarya\",\r\n" +
" \"dept\":{\r\n" +
" \"deptName\":\"eee\",\r\n" +
" \"location\":\"firstfloor\"\r\n" +
" },\r\n" +
" \"additionalDetails\":{\r\n" +
" \"projectName\":\"finalyearproject\"\r\n" +
" }\r\n" +
"}";
Request request = om.readValue(json, Request.class);
Response response = new Response(request);
String str = om.writerWithDefaultPrettyPrinter().writeValueAsString(response);
System.out.println(str);
Output
{
"name" : "iswarya",
"deptName" : "eee",
"location" : "firstfloor",
"projectName" : "finalyearproject"
}
Related
I have to handle REST calls to a suite of web services. All of those REST web services return a JSON that will have the following structure
{
"header":{
"code":"",
"value":""
},
"payload":{
"objectWithDifferentKey":{
//this object will have different structure and key depending on which web service is called by the client
}
}
}
How can I model this structure without replicating each time the header+payload structure for each object, but using something like Generics / #JsonInfo or similar?
I started with the following approach but I cannot figure out how to model the Payload object
#Getter
#Setter
#Builder
public class ResponseObject {
#JsonProperty("header")
private Header header;
#JsonProperty("payload")
private Payload payload;
}
#Getter
#Setter
#Builder
public class Header {
#JsonProperty("code")
private String code;
#JsonProperty("value")
private String value;
}
#Getter
#Setter
#Builder
public class Payload {
//don't know what to put inside and how to handle the different keys
//into the payload I always have only one object with key based on the web service I'm calling
}
#Getter
#Setter
#Builder
public class ObjectWithDifferentKey1 {
//properties inside
}
#Getter
#Setter
#Builder
public class ObjectWithDifferentKey2 {
//properties inside
}
Can someone give me an idea about that?
Your case calls for handling dynamic deserialization. One way to achieve this is by using a Map object. Another is using the JsonNode object offered by Jackson as a placeholder for your payload object. An example could be the following:
#Getter
#Setter
#NoArgsConstructor
public class Header {
#JsonProperty("code")
private String code;
#JsonProperty("value")
private String value;
}
And
#Getter
#Setter
#ToString
#NoArgsConstructor
public class ResponseObject {
private static final String EXAMPLE_1 = "{\n"
+ "\t\"header\": {\n"
+ "\t\t\"code\": \"\",\n"
+ "\t\t\"value\": \"\"\n"
+ "\t},\n"
+ "\t\"payload\": {\n"
+ "\t\t\"something\": \"value\",\n"
+ "\t\t\"something2\": \"value2\"\n"
+ "\t}\n"
+ "}";
private static final String EXAMPLE_2 = "{\n"
+ "\t\"header\": {\n"
+ "\t\t\"code\": \"\",\n"
+ "\t\t\"value\": \"\"\n"
+ "\t},\n"
+ "\t\"payload\": {\n"
+ "\t\t\"blah\": \"value\",\n"
+ "\t\t\"blah2\": \"value2\"\n"
+ "\t}\n"
+ "}";
#JsonProperty("header")
private Header header;
#JsonProperty("payload")
private JsonNode payload;
public static void main(String... args) {
var mapper = new ObjectMapper();
try {
var response1 = mapper.readValue(EXAMPLE_1, ResponseObject.class);
var response2 = mapper.readValue(EXAMPLE_2, ResponseObject.class);
var writer = new ObjectMapper().writerWithDefaultPrettyPrinter();
Stream.of(response1, response2).forEach(responseObject -> {
try {
System.out.println(writer.writeValueAsString(responseObject));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
});
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
And the output would be something like this:
{
"header" : {
"code" : "",
"value" : ""
},
"payload" : {
"something" : "value",
"something2" : "value2"
}
}
{
"header" : {
"code" : "",
"value" : ""
},
"payload" : {
"blah" : "value",
"blah2" : "value2"
}
}
That way you want have to worry about the content and the structure of the payload field. Note that this is only one way of achieving this. As of the jsonProperty annotation, this is used only for naming convention on the produced JSON.
I have access to a RESTful API which returns JSON Strings, such as the following:
{
"Container1": {
"active": true
},
"Container2": {
"active": false
},
}
The problem is that the RESTful API is a bit maldesigned. The field name contains the data already. With the Jackson library it is not possible to deserialize the field name to a property name of the corresponding Java bean class. I assume, this isn't intended by the JSON specification neither. The above JSON string needs to be deserialized to an instance of the following class:
public class Container {
private Boolean active;
private String name;
}
I end up with UnrecognizedPropertyException for the field Container1.
I thought to configure to ignore unknown properties and to provide a JsonDeserializer for that property like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Container {
private Boolean active;
private String name;
#JsonDeserialize(using = FieldNameToPropertyDeserializer.class)
public void setName(String name) {
this.name = name;
}
}
and the FieldNameToPropertyDeserializer:
public class FieldNameToPropertyDeserializer extends StdDeserializer<String> {
public FieldNameToPropertyDeserializer() {
super(String.class);
}
#Override
public String deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
return parser.getCurrentName();
}
}
The invocation of the deserialization is achieved as follows:
String jsonString = response.readEntity(String.class);
ObjectMapper objectMapper = new ObjectMapper();
ObjectReader readerFor = objectMapper.readerFor(Container.class);
MappingIterator<Container> mappingIterator = readerFor.readValues(jsonString);
while (mappingIterator.hasNext()) {
Container container = (Container) mappingIterator.next();
containers.add(container);
}
But I only receive empty objects (properties set to null) because the parsing of the properties is skipped since I set #JsonIgnoreProperties(ignoreUnknown = true).
Is this possible at all? Or should I implement something like a post-processing afterwards?
How about this. Create a class ContainerActive like this
public class ContainerActive {
private boolean active;
// constructors, setters, getters
}
And you could just do
Map<String, ContainerActive> map = mapper.readValue(jsonString, new TypeReference<Map<String, ContainerActive>>() {});
With this you will have "Container1", "Container2" as the keys and ContainerActive Object as values which has active field.
Just a quick solution, if the object is such that, that all of it object is a container object you can receive the JSON inside and JSONObject you may use below code
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestSO {
public static void main(String[] args) throws JsonParseException, JsonMappingException, JSONException, IOException {
String jsonString = "{\r\n" +
" \"Container1\": {\r\n" +
" \"active\": true\r\n" +
" },\r\n" +
" \"Container2\": {\r\n" +
" \"active\": false\r\n" +
" },\r\n" +
"}";
JSONObject jsonObject = new JSONObject(jsonString);
ObjectMapper mapper = new ObjectMapper();
for (String key : jsonObject.keySet()) {
Container container = mapper.readValue(jsonObject.get(key).toString(), Container.class);
System.out.println(container);
}
}
static class Container{
private String name;
private Boolean active;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
#Override
public String toString() {
return "Container [name=" + name + ", active=" + active + "]";
}
}
}
I've got a following JSON from API:
"hotel_data": {
"name": "Hotel Name",
"checkin_checkout_times": {
"checkin_from": "14:00",
"checkin_to": "00:00",
"checkout_from": "",
"checkout_to": "12:00"
},
"default_language": "en",
"country": "us",
"currency": "USD",
"city": "Miami"
}
I'm using Jackson library to deserialize this JSON to Java object. I don't want to create a special class for checkin_checkout_times object. I just want to get it as a plain text. Like this "checkin_from": "14:00", "checkin_to": "00:00", "checkout_from": "", "checkout_to": "12:00".
In my POJO for hotel_data this checkin_checkout_times should be as a string i.e.:
#JsonProperty("checkin_checkout_times")
private String checkinCheckoutTimes
Is this possible to get this part of the JSON as a plain text?
EDIT: Error that I'm getting com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.lang.String out of START_OBJECT token
at [Source: (String)...
Make use of JsonNode.
Just make the following setter for the field checkinCheckoutTimes in your POJO for hotel_data and it should work for you.
public void setCheckinCheckoutTimes(JsonNode node) {
this.checkinCheckoutTimes = node.toString();
}
Example
String str = "{ \"id\": 1, \"data\": { \"a\": 1 } }";
try {
System.out.println(new ObjectMapper().readValue(str,Employee.class));
} catch (IOException e) {
e.printStackTrace();
}
Where Employee is as follows:
class Employee
{
private int id;
private String data;
public Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(JsonNode node) {
this.data = node.toString();
}
#Override
public String toString() {
return "Employee{" +
"id=" + id +
", data='" + data + '\'' +
'}';
}
}
gives the following output:
Employee{id=1, data='{"a":1}'}
You can also write a custom deserializer as described in the article:
public class RawJsonDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
return mapper.writeValueAsString(node);
}
}
and then use it with annotation in your class:
public class HotelData {
#JsonProperty("checkin_checkout_times")
#JsonDeserialize(using = RawJsonDeserializer.class)
private String checkinCheckoutTimes;
// other attributes
// getters and setters
}
I'm getting a json array holding objects looking like this:
[
{
"id": "1",
"name": "some name",
"url": "some url",
"active": true
}, {
"id": "2",
"name": "some other name",
"url": "some other url",
"active": true
}
]
Now, I want to be able to deserialize that array into a java object holding a list of the objects in the array. I have made a custom deserializer looking like this:
public class ListSerializer extends JsonDeserializer<List<Provider>>{
private static final long serialVersionUID = 9114152571639338391L;
#Override
public List<Provider> deserialize(JsonParser jsonParser,
DeserializationContext arg1) throws IOException, JsonProcessingException {
// TODO Auto-generated method stub
final ObjectCodec objectCodec = jsonParser.getCodec();
final JsonNode listOrObjectNode = objectCodec.readTree(jsonParser);
final List<Provider> result = new ArrayList<Provider>();
for (JsonNode node : listOrObjectNode) {
result.add(objectCodec.treeToValue(node, Provider.class));
}
return result;
}
}
And the class holding the list looks like this:
public class ProviderList {
#JsonDeserialize(using = ListSerializer.class)
private List<Provider> providerList;
public List<Provider> getProviderList() {
return providerList;
}
public void setProviderList(final List<Provider> providerList) {
this.providerList = providerList;
}
}
I am obviously doing something wrong, because I'm getting this error:
Can not deserialize instance of
com.wirelesscar.trailser.v1_0.domain.ProviderList out of START_ARRAY
token at [Source:
[{"id":"1","name":"Posttrack","url":"http:\dev.posttrack.com","active":true},{"id":"2","name":"Trackunit","url":"http:\dev.trackunit.com","active":true}];
line: 1, column: 1]
How can I do this properly?
You can deserialize directly to a list by using the TypeReference wrapper.
#Data
public class Provider {
private Long id;
private String name;
private String url;
private boolean active;
}
#Data
public class ProviderList {
List<Provider> providerList;
}
public class JsonTest {
#Test
public void test() {
String json = "[{\n" +
" \"id\": \"1\",\n" +
" \"name\": \"some name\",\n" +
" \"url\": \"some url\",\n" +
" \"active\": true\n" +
" }, {\n" +
" \"id\": \"2\",\n" +
" \"name\": \"some other name\",\n" +
" \"url\": \"some other url\",\n" +
" \"active\": true\n" +
" }\n" +
"]";
ObjectMapper mapper = new ObjectMapper();
try {
List<Provider> providerList = mapper.readValue(json, new TypeReference<List<Provider>>(){});
for (Provider provider : providerList) {
System.out.println(provider);
}
ProviderList list = new ProviderList();
list.setProviderList(providerList);
} catch (IOException e) {
e.printStackTrace();
}
}
}
I have to deserialize following json using Jackson library into Customer class
{
"code":"C001",
"city": "Pune",
"street": "ABC Road"
}
and Classes as
class Address{
String city;
String street;
}
class Customer{
String code;
Address address;
}
I have found similar question on stack
Java jackson embedded object deserialization
but answer does not apply to my case. Also I only want to use Jackson library.
How can I map this json to Customer object?
You can put a #JsonUnwrapped annotation on the Address field in the customer class. Here is an example:
public class JacksonValue {
final static String JSON = "{\n"
+" \"code\":\"C001\",\n"
+" \"city\": \"Pune\",\n"
+" \"street\": \"ABC Road\"\n"
+"}";
static class Address {
public String city;
public String street;
#Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
static class Customer {
public String code;
#JsonUnwrapped
public Address address;
#Override
public String toString() {
return "Customer{" +
"code='" + code + '\'' +
", address=" + address +
'}';
}
}
public static void main(String[] args) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Customer.class));
}
}
Output:
Customer{code='C001', address=Address{city='Pune', street='ABC Road'}}
What you need is a custom deserializer. Jackson How-To: Custom Deserializers
For your use case it could be something like this:
class CustomerDeserializer extends JsonDeserializer<Customer>
{
public Customer deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
JsonNode node = p.getCodec().readTree(p);
String code = node.get("code").asText();
String city = node.get("city").asText();
String street = node.get("street").asText();
Address adr = new Address(city, street);
return new Customer(code, adr);
}
}
Your JSON object for a customer should look like this:
{
"code":"C001",
"address":{
"city": "Pune",
"street": "ABC Road"
}
}
Without some additional transformation this json structure can't be mapped to two classes. Either write a class CustomerAddress that will be having all three fields from json and then create Address getAddress() and Customer getCustomer() in it or transform the json to nest the address information inside the customer field as suggested by #eztam.
public CustomerAddress {
private String code;
private String city;
private String street;
public Address getAddress() {
return new Address(city, street);
}
public Address getCustomer() {
return new Customer(code, this.getAddress());
}
}
Try this !!!
{
"code":"customer1",
"address":{
"type":"nested",
"properties":{
"city":"Hyderabad",
"street":"1000ftRoad"
}
}
}