[This is not a duplicate of Can not instantiate value of type from JSON String; no single-String constructor/factory method: that is a far simpler POJO and JSON. The solution in my case was different as well.]
The JSON I want to parse and create a POJO from:
{
"test_mode": true,
"balance": 1005,
"batch_id": 99,
"cost": 1,
"num_messages": 1,
"message": {
"num_parts": 1,
"sender": "EXAMPL",
"content": "Some text"
},
"receipt_url": "",
"custom": "",
"messages": [{
"id": 1,
"recipient": 911234567890
}],
"status": "success"
}
If the response happens to be an error, it looks like:
{
"errors": [{
"code": 80,
"message": "Invalid template"
}],
"status": "failure"
}
Here's the POJO I have defined:
#Data
#Accessors(chain = true)
public class SmsResponse {
#JsonProperty(value = "test_mode")
private boolean testMode;
private int balance;
#JsonProperty(value = "batch_id")
private int batchId;
private int cost;
#JsonProperty(value = "num_messages")
private int numMessages;
private Message message;
#JsonProperty(value = "receipt_url")
private String receiptUrl;
private String custom;
private List<SentMessage> messages;
private String status;
private List<Error> errors;
#Data
#Accessors(chain = true)
public static class Message {
#JsonProperty(value = "num_parts")
private int numParts;
private String sender;
private String content;
}
#Data
#Accessors(chain = true)
public static class SentMessage {
private int id;
private long recipient;
}
#Data
#Accessors(chain = true)
public static class Error {
private int code;
private String message;
}
}
The annotations #Data (tells Lombok to automatically generate getters, setters, toString() and hashCode() methods for the class) and #Accessors (tells Lombok to generate the setters in such a way that they can be chained) are from Project Lombok.
Seems like a straightforward setup, but every time I run:
objectMapper.convertValue(response, SmsResponse.class);
I get the error message:
Can not instantiate value of type [simple type, class com.example.json.SmsResponse]
from String value ... ; no single-String constructor/factory method
Why do I need a single-string constructor for SmsResponse, and if so, which string do I accept in it?
To parse and map a JSON String with ObjectMapper you need to use the readValue method:
objectMapper.readValue(response, SmsResponse.class);
Related
I am trying to convert the JSON timestamp object into Java
I have researched converting a JSON string to java but unsure what I'm looking for.
I can get it to work if the JSON uses an array but unfortunately it does not use this approach.
JSON Payload
{
"type": "RFID-read",
"event": {
"id": "3892fec6-9246-4699-ba86-99ab1df369a9",
"timestamp": "2020-11-19T15:01:11.391+0000",
"deviceId": "FX9600FB2D21",
"data": {
"format": "epc",
"id": "000000000000000000000115",
"reads": 1,
"rssi": -72,
"antennaId": "1"
}
},
"analytics": {
"tenant": "73876942a20c12550f996b2152e5ca9e",
"resourceId": "000000000000000000000115",
"location": "FX9600FB2D21",
"timestamp": "2020-11-19T15:01:11.391+0000",
"meta": {
"type": "inventory"
}
}
}
Event DTO
#Data
#Builder(toBuilder = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ZebraEventReadsDto {
private String id;
private ZonedDateTime timestamp;
#NotNull
#Size(min = 1, max = 100)
private String deviceId;
private String format;
ZebraDataReadsDto data;
Update DTO
#Data
#Builder(toBuilder = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ZebraLocationUpdateDto {
private String type;
ZebraEventReadsDto event;
public static List<LocationUpdateDom> toDomainModel(ZebraLocationUpdateDto zebraLocationUpdateDto) {
List<LocationUpdateDom> locationUpdateDomList = new ArrayList<>();
locationUpdateDomList.add(LocationUpdateDom.builder()
.deviceName(zebraLocationUpdateDto.zebraEventReadsDto.getDeviceId())
.dateTime(zebraLocationUpdateDto.zebraEventReadsDto.getTimestamp())
.tagId(zebraLocationUpdateDto.zebraEventReadsDto.data.getId())
.latLng(Optional.empty())
.build());
return locationUpdateDomList;
}
}
To resolving the parsing error I used the following annotation above my timestamp variable.
#JSONFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
private ZonedDateTime timestamp;
I can now return 200 OK
I have this JSON String send by Angular:
{
"transaction_id": "1234",
"usage": "Test Usage",
"billing_address": {
"first_name": "name",
"last_name": "name",
"address1": "street 1234",
"zip_code": "11923"
},
"shipping_address": {
"first_name": "name",
"last_name": "name",
"address1": "street 1234",
"zip_code": "11923"
}
}
Java code:
public class DTO {
private String transaction_id;
private String usage;
private BillingAddress billingAddress;
private ShippingAddress shippingAddress;
... getter/setter
}
public class BillingAddress {
private String firstName;
private String lastName;
private String address1;
private String zip_code;
... getter/setter
}
public class ShippingAddress {
private String firstName;
private String lastName;
private String address1;
private String zip_code;
... getter/setter
}
Spring endpoint:
#PostMapping(value = "/{id}", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<?> handleWpfMessage(#PathVariable("id") id,
#RequestBody DTO data){
....
}
What is the proper way to map the inner objects for billing_address and shipping_address in order values to be mapped properly? Do I need to add annotations in order to map them properly?
You should add the following annotations to your DTO class:
public class DTO {
private String transaction_id;
private String usage;
#JsonProperty("billing_address")
private BillingAddress billingAddress;
#JsonProperty("shipping_address")
private ShippingAddress shippingAddress;
... getter/setter
}
Your angular client uses snake case. In order to make jackson deserializing properly you can configure it globally with :
spring.jackson.property-naming-strategy=SNAKE_CASE
However you can also configure it for a specific class :
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class DTO {
}
As already mentioned, you can use the Jackson property mapping annotaion in your DTO class.
#JsonProperty("billing_address")
private BillingAddress billingAddress;
This means, in the json, attribute billing_address will be assigned to billingAddress variable.
Unable to map json string with java object, getting error JSON parse error: Can not construct instance of com.test.CPInput$Evc$Uni
error:
{
"timestamp": 1502270576300,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "**JSON parse error: Can not construct instance of com.test.CPInput$Evc$Uni: can only instantiate non-static inner class by using default, no-argument constructor; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.test.CPInput$Evc$Uni: can only instantiate non-static inner class by using default, no-argument constructor at [Source: java.io.PushbackInputStream#edc246; line: 20, column: 9] (through reference chain: com.test.CPInput["evc"]->com.test.CPInput$Evc["uni"]->java.util.ArrayList[0]**)",
"path": "/demo/addCustomer"
}
json
{
"customerId": "abcdef",
"customerSegment": {
"customerType": "customer type",
"customerSubtype": "subtype",
"industry": "industry",
"subIndustry": "subindustry",
"specialPopulation": " spl population"
},
"evc": {
"evcId": "evcid",
"action": "PR",
"serviceId": "assigned prod id",
"previousValues": {
"previousName":"evcId",
"previousValue":"EVC id - OLD"
},
"uni": [
{
"type": "uniA",
"uniId": "uni id",
"siteId": "sidt id",
"tspCode": "tsp code",
"elocId": "e loc id",
"siteClli": "site clli",
"uniAction": "PR",
"previousValues": {
"previousName":"evcId",
"previousValue":"EVC id - OLD"
}
},
{
"type": "uniZ",
"siteId": "sidt id",
"tspCode": "tsp code",
"elocId": "e loc id",
"siteClli": "site clli",
"uniAction": "PR",
"previousValues": {
"previousName":"evcId",
"previousValue":"EVC id - OLD"
}
}
]
},
"solutionType": "EPL",
"source": "Orion"
}
CPInput.java
public class CPInput {
private String customerId;
private CustomerSegment customerSegment;
private Evc evc;
private String solutionType;
private String source;
public CPInput() {
super();
}
public class CustomerSegment{
private String customerType;
private String customerSubtype;
private String industry;
private String subIndustry;
private String specialPopulation;
//getter setter
}
public class Evc{
private String evcId;
private String action;
private String serviceId;
private PreviousValues previousValues;
private List<CPInput.Evc.Uni> uni=new ArrayList<CPInput.Evc.Uni>();
//getter setter
public class PreviousValues{
private String previousName;
private String previousValue;
//getter setter
}
public class Uni{
private String type;
private String uniId;
private String siteId;
private String tspCode;
private String elocId;
private String siteClli;
private String uniAction;
private PreviousValues previousValues;
//getter setter
public class PreviousValues{
private String previousName;
private String previousValue;
//getter setter
}
//getter setter
}
DemoController.java
#RestController
#SpringBootApplication(scanBasePackages = {"com.test"})
#RequestMapping(value = "/demo")
public class DemoController {
#RequestMapping(method = RequestMethod.POST, value = "/addCustomer")
public CPOutput addCustomer(#RequestBody CPInput input) {
System.out.println(input);
return null;
}
}
Try to use static inner classes for: CustomerSegment, Evc, PreviousValues and Uni.
I always use static inner classes, and I don't have any problem like that.
I've created a service with this specification:
#RequestMapping(value = "initCustomer", method = RequestMethod.POST)
public ResponseEntity<Long> create(#RequestBody CustomerForm customerForm) {
The element CustomerForm have this structure (i omit every getter/setter method):
#XmlRootElement(name = "customer")
public static class CustomerForm {
private String name;
private String hostname;
private List<ProbeMonitor> monitors;
#XmlElement(name = "monitor")
public List<ProbeMonitor> getMonitors() {
return monitors;
}
}
The class ProbeMonitor is an #Entity with an #EmbeddedId (because this classe have more than one field in the primary key).
public class ProbeMonitor implements Serializable {
private static final long serialVersionUID = 1L;
private ProbeMonitorId id;
private Integer active;
private Date inserted;
private Date updated;
#EmbeddedId
public ProbeMonitorId getId() {
return id;
}
}
And finally, ProbeMonitorId:
#Embeddable
public class ProbeMonitorId implements Serializable {
private static final long serialVersionUID = 1L;
private String customer;
private String name;
private String type;
}
Now i should make a request to this service (using RestTemplate), but first i'm trying to use a simple REST client where i send JSON (to check that all works).
I'm sending JSON in this format, but i get "Unrecognized field "id""
{
"name": "test_name",
"hostname": "test_hosT",
"monitors": [
{ "id": {"customer": "custom"},
"active": "1"
}
]
}
I've tried to remove the "id" fields in JSON request and WebService will be invoked correctly.
How "monitors" should be formatted?
And... (this is the second question) how i should create the RestTemplate to call this?
I think to have found the problems:
1 - private ProbeMonitorId id; was not declared in JSON scope:
#EmbeddedId
**#XmlElement**
#**JsonProperty**
public ProbeMonitorId getId() {
return id;
}
2 - Inserted and updated was not declared to be ignored
#Basic
#Column(name = "Dt_Insert")
#JsonIgnore
public Date getInserted() {
return inserted;
}
3 - Correct JSON sent:
{
"name": "test_name",
"hostname": "test_hosT",
"monitors": [{
"id": {"customer": "cst", "name": "aname", "type": "atype"},
"active": "1"
}
]
I have a class that should be deserialized accordingly the header request.
If header is on V1 version, ww should output the information field of Product class, like a String. Otherwise it output an Info object.
Is there another solution to do this, instead duplicate the class?
public class Product{
private String name;
private Integer id;
private Info information;
}
public class Info{
private String generalInfo;
private String fullDescription;
private String code;
}
public class Product{
private String name;
private Integer id;
private String information;
}
Above the JSON when use INFO object and when information is a string.
{
"name": "Paul",
"id": "123123,
"information": {
"generalInfo":"Business Product",
"fullDescription":"23",
"code":"9487987289929222-3"
}
}
{
"name": "Paul",
"id": "123123,
"information": "Business Product - 23 - 9487987289929222-3 "
}