json deserialization -- StackOverflowException - json
I'm just trying to parse a json input, which is an implementation of AbstractList, containing custom objects. But, for some reason, when it hits the deserializer for CustomerDealerRecord, it never gets past the first node, and then ends up throwing a StackOverflowException. I've been pounding at this for like 4 hours, and I've done numerous different Google searches to no avail. So, my last ditch effort is coming here. Any light you all can shed on this will be much appreciated. Thank you. The code is below.
public class PhoneDeserializer extends StdDeserializer<Phone>{
public PhoneDeserializer() {
super(Phone.class);
}
#SuppressWarnings("unchecked")
#Override
public Phone deserialize(JsonParser jp,
DeserializationContext ctx) throws IOException,
JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
return mapper.readValue(jp, Phone.class);
}
}
--
public class CustomerDealerRecordDeserializer extends StdDeserializer<CustomerDealerRecord>{
public CustomerDealerRecordDeserializer() {
super(CustomerDealerRecord.class);
}
#SuppressWarnings("unchecked")
#Override
public CustomerDealerRecord deserialize(JsonParser jp,
DeserializationContext ctx) throws IOException,
JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
return mapper.readValue(jp, CustomerDealerRecord.class);
}
}
-- This is the Custom List
public class CustomerDealerRecordList extends AbstractList<CustomerDealerRecord> {
private List<CustomerDealerRecord> records = new ArrayList<CustomerDealerRecord>();
#Override
public CustomerDealerRecord get(int index) {
// TODO Auto-generated method stub
return records.get(index);
}
#Override
public int size() {
// TODO Auto-generated method stub
return records.size();
}
public boolean add(CustomerDealerRecord cdr){
return records.add(cdr);
}
}
-- This is the Controller method
#SuppressWarnings("unchecked")
public String getCustomerDealerReportAsExcel(HttpServletRequest req, HttpServletResponse resp){
VelocityContext vc = new VelocityContext();
vc.put("response", resp);
// Jackson stuff
CustomerDealerRecordDeserializer deser = new CustomerDealerRecordDeserializer();
PhoneDeserializer phoneDeser = new PhoneDeserializer();
// We have to create a module for the alias'ed class
SimpleModule cdrModule = new SimpleModule("CustomerDealerRecord", new Version(1,0,0,null));
SimpleModule phoneModule = new SimpleModule("Phone", new Version(1,0,0,null));
// Add the deserializer to the module
cdrModule.addDeserializer(CustomerDealerRecord.class, deser);
phoneModule.addDeserializer(Phone.class, phoneDeser);
// Now, create our mapper and then register the module to it.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(cdrModule);
mapper.registerModule(phoneModule);
CustomerDealerRecordList list = null;
try {
JsonParser jp = mapper.getJsonFactory().createJsonParser(req.getParameter("json"));
list = mapper.readValue(jp, CustomerDealerRecordList.class);
} catch (JsonParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JsonMappingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return format(req, vc, "reports/customer_dealer_report_excel");
}
-- This is the Phone Model object
public class Phone {
public Phone(String areaCode, String phone){
this.areaCode = areaCode;
this.phoneNumber = phone;
}
private String areaCode;
private String phoneNumber;
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
-- This is the CustomerDealerRecord Model object
public class CustomerDealerRecord {
private String fleetName;
private String fleetNumber;
private String dealerName;
private String dealerNumber;
private String territoryName;
private String territoryNumber;
private String city;
private String state;
private Date downTime;
private String failureDescription;
private String tireManufacturer;
private String tireSize;
private String tireType;
private String tirePosition;
private String category;
private String callerName;
private Phone callerPhone;
private String caseNumber;
private Date caseCloseDate;
private String poNumber;
private String truckNumber;
private String trailerNumber;
private String tractorNumber;
private String serviceDetailStatus;
private String refusalReason;
public String getFleetName() {
return fleetName;
}
public void setFleetName(String fleetName) {
this.fleetName = fleetName;
}
public String getFleetNumber() {
return fleetNumber;
}
public void setFleetNumber(String fleetNumber) {
this.fleetNumber = fleetNumber;
}
public String getDealerName() {
return dealerName;
}
public void setDealerName(String dealerName) {
this.dealerName = dealerName;
}
public String getDealerNumber() {
return dealerNumber;
}
public void setDealerNumber(String dealerNumber) {
this.dealerNumber = dealerNumber;
}
public String getTerritoryName() {
return territoryName;
}
public void setTerritoryName(String territoryName) {
this.territoryName = territoryName;
}
public String getTerritoryNumber() {
return territoryNumber;
}
public void setTerritoryNumber(String territoryNumber) {
this.territoryNumber = territoryNumber;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Date getDownTime() {
return downTime;
}
public void setDownTime(Date downTime) {
this.downTime = downTime;
}
public String getFailureDescription() {
return failureDescription;
}
public void setFailureDescription(String failureDescription) {
this.failureDescription = failureDescription;
}
public String getTireManufacturer() {
return tireManufacturer;
}
public void setTireManufacturer(String tireManufacturer) {
this.tireManufacturer = tireManufacturer;
}
public String getTireSize() {
return tireSize;
}
public void setTireSize(String tireSize) {
this.tireSize = tireSize;
}
public String getTireType() {
return tireType;
}
public void setTireType(String tireType) {
this.tireType = tireType;
}
public String getTirePosition() {
return tirePosition;
}
public void setTirePosition(String tirePosition) {
this.tirePosition = tirePosition;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getCallerName() {
return callerName;
}
public void setCallerName(String callerName) {
this.callerName = callerName;
}
public Phone getCallerPhone() {
return callerPhone;
}
public void setCallerPhone(Phone callerPhone) {
this.callerPhone = callerPhone;
}
public String getCaseNumber() {
return caseNumber;
}
public void setCaseNumber(String caseNumber) {
this.caseNumber = caseNumber;
}
public Date getCaseCloseDate() {
return caseCloseDate;
}
public void setCaseCloseDate(Date caseCloseDate) {
this.caseCloseDate = caseCloseDate;
}
public String getPoNumber() {
return poNumber;
}
public void setPoNumber(String poNumber) {
this.poNumber = poNumber;
}
public String getTruckNumber() {
return truckNumber;
}
public void setTruckNumber(String truckNumber) {
this.truckNumber = truckNumber;
}
public String getTrailerNumber() {
return trailerNumber;
}
public void setTrailerNumber(String trailerNumber) {
this.trailerNumber = trailerNumber;
}
public String getTractorNumber() {
return tractorNumber;
}
public void setTractorNumber(String tractorNumber) {
this.tractorNumber = tractorNumber;
}
public String getServiceDetailStatus() {
return serviceDetailStatus;
}
public void setServiceDetailStatus(String serviceDetailStatus) {
this.serviceDetailStatus = serviceDetailStatus;
}
public String getRefusalReason() {
return refusalReason;
}
public void setRefusalReason(String refusalReason) {
this.refusalReason = refusalReason;
}
}
-- Sample JSON
[
{
"fleetName":"sycamore specialzed carriers",
"fleetNumber":"CF00002760",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":null,
"state":null,
"downTime":"3000-01-01",
"failureDescription":null,
"tireManufacturer":"Continental Tire",
"tireSize":"10.00R15",
"tireType":"DRIVE",
"tirePosition":"LFO",
"category":"Dealer Location Information",
"callerName":"ANN RENNER",
"callerPhone":{
"areaCode":null,
"phoneNumber":null
},
"caseNumber":"189354",
"caseCloseDate":null,
"poNumber":null,
"truckNumber":null,
"trailerNumber":null,
"tractorNumber":null,
"serviceDetailStatus":"CAN",
"refusalReason":"Response time"
},
{
"fleetName":"sycamore specialzed carriers",
"fleetNumber":"CF00002760",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":null,
"state":null,
"downTime":"3000-01-01",
"failureDescription":null,
"tireManufacturer":null,
"tireSize":null,
"tireType":null,
"tirePosition":null,
"category":"Dealer Location Information",
"callerName":"ANN RENNER",
"callerPhone":{
"areaCode":null,
"phoneNumber":null
},
"caseNumber":"189354",
"caseCloseDate":null,
"poNumber":null,
"truckNumber":null,
"trailerNumber":null,
"tractorNumber":null,
"serviceDetailStatus":"ACT",
"refusalReason":null
},
{
"fleetName":"sycamore specialzed carriers",
"fleetNumber":"CF00002760",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":null,
"state":null,
"downTime":"3000-01-01",
"failureDescription":null,
"tireManufacturer":"Continental Tire",
"tireSize":"295/75R22.5",
"tireType":"BIAS",
"tirePosition":"LMI",
"category":"Service Call",
"callerName":" ",
"callerPhone":{
"areaCode":null,
"phoneNumber":null
},
"caseNumber":"189240",
"caseCloseDate":null,
"poNumber":null,
"truckNumber":null,
"trailerNumber":null,
"tractorNumber":null,
"serviceDetailStatus":"CAN",
"refusalReason":"Other"
},
{
"fleetName":"sycamore specialzed carriers",
"fleetNumber":"CF00002760",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":null,
"state":null,
"downTime":"3000-01-01",
"failureDescription":null,
"tireManufacturer":"Continental Tire",
"tireSize":"295/75R22.5",
"tireType":"DRIVE",
"tirePosition":"LMI",
"category":"Service Call",
"callerName":" ",
"callerPhone":{
"areaCode":null,
"phoneNumber":null
},
"caseNumber":"189240",
"caseCloseDate":null,
"poNumber":null,
"truckNumber":null,
"trailerNumber":null,
"tractorNumber":null,
"serviceDetailStatus":"ACT",
"refusalReason":null
},
{
"fleetName":"TEST CUSTOMER",
"fleetNumber":"123ee22a",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":null,
"state":null,
"downTime":"3000-01-01",
"failureDescription":null,
"tireManufacturer":null,
"tireSize":null,
"tireType":null,
"tirePosition":null,
"category":"Service Call",
"callerName":"JASON MA",
"callerPhone":{
"areaCode":"123",
"phoneNumber":"222"
},
"caseNumber":"189328",
"caseCloseDate":"2012-01-03",
"poNumber":null,
"truckNumber":null,
"trailerNumber":null,
"tractorNumber":null,
"serviceDetailStatus":"ACT",
"refusalReason":"Other"
},
{
"fleetName":"TEST CUSTOMER",
"fleetNumber":"123ee22a",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":"ST LOUIS",
"state":"MO",
"downTime":"3000-01-01",
"failureDescription":"REPAIR IF POSSIBLE",
"tireManufacturer":"Continental Tire",
"tireSize":"11R22.5",
"tireType":"RADIAL",
"tirePosition":"LRI",
"category":"Service Call",
"callerName":"BJ TEST",
"callerPhone":{
"areaCode":"314",
"phoneNumber":"592-3129"
},
"caseNumber":"189341",
"caseCloseDate":"2012-06-19",
"poNumber":null,
"truckNumber":null,
"trailerNumber":"34",
"tractorNumber":"12",
"serviceDetailStatus":"CAN",
"refusalReason":"Product not available"
},
{
"fleetName":"TEST CUSTOMER",
"fleetNumber":"123ee22a",
"dealerName":null,
"dealerNumber":null,
"territoryName":null,
"territoryNumber":null,
"city":"ST LOUIS",
"state":"MO",
"downTime":"3000-01-01",
"failureDescription":"REPAIR IF POSSIBLE",
"tireManufacturer":"Continental Tire",
"tireSize":"11R22.5",
"tireType":"RADIAL",
"tirePosition":"LRI",
"category":"Service Call",
"callerName":"BJ TEST",
"callerPhone":{
"areaCode":"314",
"phoneNumber":"592-3129"
},
"caseNumber":"189341",
"caseCloseDate":"2012-06-19",
"poNumber":null,
"truckNumber":null,
"trailerNumber":"34",
"tractorNumber":"12",
"serviceDetailStatus":"ACT",
"refusalReason":null
}
]
As #bmargulies commented, the PhoneDeserializer calls (indirectly) the PhoneDeserializer. The line mapper.readValue(jp, Phone.class); will let Jackson resolve the deserializer for the Phone class, which happens to be PhoneDeserializer. You don't need the Phonedeserializer class at all, Jackson will handle your phone class properly.
Yeah, initially I didn't use the deserializer, and as #pgelinas said, it wasn't needed. That was where I got after playing around with things. It didn't work. The underlying problem was that I had to go back to using mapper.convertValue and give the Phone class a public no arg constructor. Basically, I'm an idiot and was just over-analyzing the simple.
Related
Convert POJO to a GSON to JSON
I'm trying to send a JSON post request to https://graph.microsoft.com/v1.0/me/sendMail to send an email. I created a POJO with getters and setters so I can set the json value dynamically. SendEmail.java import java.util.ArrayList; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.annotation.JsonProperty; public class SendEmail { public class Body{ public String contentType; public String content; public void setContentType(String contentType) { this.contentType = contentType; } public String getContentType() { return contentType; } public void setContent(String content) { this.content = content; } public String getContent() { return content; } } public class CcRecipient{ public EmailAddress emailAddress; public void setEmailAddress(SendEmail.EmailAddress emailAddress) { this.emailAddress = emailAddress; } public SendEmail.EmailAddress getEmailAddress() { return emailAddress; } } public class EmailAddress{ public String address; public void setAddress(String address) { this.address = address; } public String getAddress() { return address; } } public class Message{ public String subject; public Body body; public ArrayList<ToRecipient> toRecipients; public ArrayList<CcRecipient> ccRecipients; public void setSubject(String subject) { this.subject = subject; } public String getSubject() { return subject; } public void setBody(SendEmail.Body body) { this.body = body; } public SendEmail.Body getBody() { return body; } public void setToRecipients(ArrayList<SendEmail.ToRecipient> toRecipients) { this.toRecipients = toRecipients; } public ArrayList<SendEmail.ToRecipient> getToRecipients() { return toRecipients; } public void setCcRecipients(ArrayList<SendEmail.CcRecipient> ccRecipients) { this.ccRecipients = ccRecipients; } public ArrayList<SendEmail.CcRecipient> getCcRecipients() { return ccRecipients; } } public class Root{ public Message message; public String saveToSentItems; public void setMessage(SendEmail.Message message) { this.message = message; } public SendEmail.Message getMessage() { return message; } public void setSaveToSentItems(String saveToSentItems) { this.saveToSentItems = saveToSentItems; } public String getSaveToSentItems() { return saveToSentItems; } } public class ToRecipient{ public EmailAddress emailAddress; public void setEmailAddress(SendEmail.EmailAddress emailAddress) { this.emailAddress = emailAddress; } public SendEmail.EmailAddress getEmailAddress() { return emailAddress; } } } Below is the test class. The jsonRequest is empty. How can I put the correct JSON values using my POJO? Test.java SendEmail sendEmail = new SendEmail(); SendEmail.Message message = sendEmail.new Message(); message.setSubject("Test subject"); SendEmail.Body body = sendEmail.new Body(); body.setContent("this is a test body on an email"); SendEmail.ToRecipient toRecipient = sendEmail.new ToRecipient(); SendEmail.EmailAddress emailAddress = sendEmail.new EmailAddress(); emailAddress.setAddress("dummyemail#outlook.com"); toRecipient.setEmailAddress(emailAddress); String accessToken = connectEmail(); System.out.println("ACCESS TOKEN: "+accessToken); HttpURLConnection connection = null; Gson gson = new Gson(); String jsonRequest = gson.toJson(toRecipient); try { URL url = new URL("https://graph.microsoft.com/v1.0/me/sendMail"); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Authorization","Bearer "+accessToken); connection.setRequestProperty("Content-Type", "application/json"); //connection.setRequestProperty("Accept", "text/plain"); connection.setUseCaches(false); connection.setDoOutput(true); try(OutputStream os = connection.getOutputStream()) { System.out.println("jsonRequest: "+jsonRequest); os.write(jsonRequest.getBytes()); } } catch (Exception e) { System.out.println("EXCEPTION SENDING EMAIL"); } Below is the sample post request I need to write using values from the POJO. { "message": { "subject": "Meet for lunch?", "body": { "contentType": "Text", "content": "The new cafeteria is open." }, "toRecipients": [ { "emailAddress": { "address": "frannis#contoso.onmicrosoft.com" } } ], "ccRecipients": [ { "emailAddress": { "address": "danas#contoso.onmicrosoft.com" } } ] }, "saveToSentItems": "false" }
how to map json data which has dynamic fields like sku0,sku1 into a pojo class in java
below is my api response { "skuInventory": { "sku0": { "inventory": { "PODate": { "time": 1674363600000 }, "checkBackOrderQTY": false, "allocatedQuantity": 127, "endDate": { "time": 1669216432575 }, "balanceQuantity": 4096, "ltr60Days": true, "modifiedDate": null, "availableQuantity": 0, "id": "VS-1261", "dateFirstReceived": { "time": 1136178000000 }, "totalOnOrder": 4858, "remainDaysCurrDatePODate": 52, "pendingBackorders": 0, "presellFlag": false, "storeInventory": true }, "quantityLimitWebPageMsg": "", "freeShippingPromoAmt": 25, "notCartableBrandOOSMsg": "", "cartableFlags": { "bopusOnlyMessage": "Item is unavailable for shipping, please check local stores for pickup availability", "ADP": "0", "BOPUS": "1", "consumeUpdateFlexShippingVerbiage": "true", "DTCEstShippingMessage": "Temporarily Out of Stock", "isProductFlexShippingFeeApplied": "false", "cartableSku": "1", "DTC": "0", "DTCAvailablityMessage": "Temporarily Out of Stock", "isProductFlexShippingFeeWaived": "false", "DTCEstShipMsgSiteExp": "Temporarily Out of Stock", "DTCAvMsgPDPRedesign": "Temporarily Out of Stock" }, "quantityThreshold": 0 } } } As we can see from above json structure there are multiple properties like sku0,sku1.sku2 I want to convert this json into POJO which i have created which is like below, public class Root { private SkuInventory skuInventory; public SkuInventory getSkuInventory() { return skuInventory; } public void setSkuInventory(SkuInventory skuInventory) { this.skuInventory = skuInventory; } } public class SkuInventory { //#JsonAlias({ "sku0", "sku1", "sku2" }) private List<Sku0> sku0; public List<Sku0> getSku0() { return sku0; } public void setSku0(List<Sku0> sku0) { this.sku0 = sku0; } } public class Sku { private Inventory inventory; private String quantityLimitWebPageMsg; private int freeShippingPromoAmt; private String notCartableBrandOOSMsg; private CartableFlags cartableFlags; private int quantityThreshold; public Inventory getInventory() { return inventory; } public void setInventory(Inventory inventory) { this.inventory = inventory; } public String getQuantityLimitWebPageMsg() { return quantityLimitWebPageMsg; } public void setQuantityLimitWebPageMsg(String quantityLimitWebPageMsg) { this.quantityLimitWebPageMsg = quantityLimitWebPageMsg; } public int getFreeShippingPromoAmt() { return freeShippingPromoAmt; } public void setFreeShippingPromoAmt(int freeShippingPromoAmt) { this.freeShippingPromoAmt = freeShippingPromoAmt; } public String getNotCartableBrandOOSMsg() { return notCartableBrandOOSMsg; } public void setNotCartableBrandOOSMsg(String notCartableBrandOOSMsg) { this.notCartableBrandOOSMsg = notCartableBrandOOSMsg; } public CartableFlags getCartableFlags() { return cartableFlags; } public void setCartableFlags(CartableFlags cartableFlags) { this.cartableFlags = cartableFlags; } public int getQuantityThreshold() { return quantityThreshold; } public void setQuantityThreshold(int quantityThreshold) { this.quantityThreshold = quantityThreshold; } } public class Inventory { #JsonProperty("PODate") private Time pODate; private boolean checkBackOrderQTY; private int allocatedQuantity; private Time endDate; private int balanceQuantity; private boolean ltr60Days; private Object modifiedDate; private int availableQuantity; private String id; private Time dateFirstReceived; private int totalOnOrder; private int remainDaysCurrDatePODate; private int pendingBackorders; private boolean presellFlag; private boolean storeInventory; public Time getpODate() { return pODate; } public void setpODate(Time pODate) { this.pODate = pODate; } public boolean isCheckBackOrderQTY() { return checkBackOrderQTY; } public void setCheckBackOrderQTY(boolean checkBackOrderQTY) { this.checkBackOrderQTY = checkBackOrderQTY; } public int getAllocatedQuantity() { return allocatedQuantity; } public void setAllocatedQuantity(int allocatedQuantity) { this.allocatedQuantity = allocatedQuantity; } public Time getEndDate() { return endDate; } public void setEndDate(Time endDate) { this.endDate = endDate; } public int getBalanceQuantity() { return balanceQuantity; } public void setBalanceQuantity(int balanceQuantity) { this.balanceQuantity = balanceQuantity; } public boolean isLtr60Days() { return ltr60Days; } public void setLtr60Days(boolean ltr60Days) { this.ltr60Days = ltr60Days; } public Object getModifiedDate() { return modifiedDate; } public void setModifiedDate(Object modifiedDate) { this.modifiedDate = modifiedDate; } public int getAvailableQuantity() { return availableQuantity; } public void setAvailableQuantity(int availableQuantity) { this.availableQuantity = availableQuantity; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Time getDateFirstReceived() { return dateFirstReceived; } public void setDateFirstReceived(Time dateFirstReceived) { this.dateFirstReceived = dateFirstReceived; } public int getTotalOnOrder() { return totalOnOrder; } public void setTotalOnOrder(int totalOnOrder) { this.totalOnOrder = totalOnOrder; } public int getRemainDaysCurrDatePODate() { return remainDaysCurrDatePODate; } public void setRemainDaysCurrDatePODate(int remainDaysCurrDatePODate) { this.remainDaysCurrDatePODate = remainDaysCurrDatePODate; } public int getPendingBackorders() { return pendingBackorders; } public void setPendingBackorders(int pendingBackorders) { this.pendingBackorders = pendingBackorders; } public boolean isPresellFlag() { return presellFlag; } public void setPresellFlag(boolean presellFlag) { this.presellFlag = presellFlag; } public boolean isStoreInventory() { return storeInventory; } public void setStoreInventory(boolean storeInventory) { this.storeInventory = storeInventory; } } Below is the codee to map json into a POJO ResponseEntity<?> inventoryData = (ResponseEntity<?>) inventoryAPIResponse.getData(); Map<?, ?> inventoryBody = (Map<?, ?>) inventoryData.getBody(); Root atgInventory = getObjectMapper().convertValue(inventoryBody, Root.class); Sku availableQuantity = (Sku) atgInventory.getSkuInventory().getSku(); If we execute above code we get errors like this java.lang.IllegalArgumentException: Cannot deserialize value of type `java.util.ArrayList<Sku>` from Object value (token `JsonToken.START_OBJECT`) at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: Root["skuInventory"]->SkuInventory["sku0"]) How can wee resolve this issue of mapping a json to a pojo which has a property which is dynamic like sku0,sku1,sku2 etc??
I would introduce a Map as follows: public class Root { private Map<String, Sku> skuInventory; } This Map will then replace your SkuInventory object and has keys like Sku0, Sku1 etc.
Can Jackson be configured to deserialize the JSON Key (Not value)?
I am trying to trim the JSON key in order to avoid spaces in the JSON requests. JSON Object would look like with white space, (check for "eq") { "page": 0, "size": 25, "and":{ "eq ": [ { "field":"id", "value": "60536" } ] } } I find lot of solution ranging from SimpleModule to JsonDeserializer but all generally work on the value part. How can I trim the key itself which then correctly converts into my Java POJO? public class SearchInDTO implements InDTO { private Integer pageNo; private Integer pageSize; private ANDFilter andFilter; #JsonProperty("page") public Integer getPageNo() { return pageNo; } public void setPageNo(Integer pageNo) { this.pageNo = pageNo; } #JsonProperty("size") public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } #JsonProperty("and") public ANDFilter getAndFilter() { return andFilter; } public void setAndFilter(ANDFilter andFilter) { this.andFilter = andFilter; } public static class EQFilter extends FieldValue { #JsonProperty("field") public String getFieldName() { return super.getFieldName(); } #JsonProperty("value") public String getValue() { return super.getValue(); } #Override public String toString() { final StringBuilder sb = new StringBuilder("EQFilter{"); sb.append(super.toString()); sb.append('}'); return sb.toString(); } } public static class FieldValue { private String fieldName; private String value; #JsonProperty("field") public String getFieldName() { return fieldName; } public void setFieldName(String fieldName) { this.fieldName = fieldName; } #JsonProperty("value") public String getValue() { if(value == null) return value; return value.toLowerCase(); } public void setValue(String value) { this.value = value; } } public static class ANDFilter { private List<EQFilter> eqFilters = new ArrayList<>(); #JsonProperty("eq") public List<EQFilter> getEqFilters() { return eqFilters; } public void setEqFilters(List<EQFilter> eqFilters) { this.eqFilters = eqFilters; } } }
Solution with custom JsonParser implementation: public class MyJsonParser extends JsonParserDelegate { public MyJsonParser(JsonParser parser) { super(parser); } #Override public String getCurrentName() throws IOException { return super.getCurrentName().trim(); } } public class MyJsonParserFactory extends MappingJsonFactory { #Override protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException { return new MyJsonParser(super._createParser(in, ctxt)); } #Override protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { return new MyJsonParser(super._createParser(r, ctxt)); } #Override protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable) throws IOException { return new MyJsonParser(super._createParser(data, offset, len, ctxt, recyclable)); } #Override protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException { return new MyJsonParser(super._createParser(data, offset, len, ctxt)); } #Override protected JsonParser _createParser(DataInput input, IOContext ctxt) throws IOException { return new MyJsonParser(super._createParser(input, ctxt)); } } #Component public class MyJackson2ObjectMapperBuilderCustomizer implements Jackson2ObjectMapperBuilderCustomizer { #Override public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { jacksonObjectMapperBuilder.factory(new MyJsonParserFactory()); } }
How to retrieve field value from json object when object name has DOT in it
I want to extract "msg" value from below json using fasterxml.jackson - Can anyone suggest me how my model class should look like? { "statusCode": 422, "error": "Unprocessable Entity", "message": "Bad data received", "err_data": { "payment_details.type": { "location": "body", "param": "payment_details.type", "msg": "Must be either etransfer or cheque" } } } This is what I have done, but it is always returning "null" ! #JsonInclude(JsonInclude.Include.ALWAYS) public class MyApiResponse extends ParentResponse implements Serializable { private static final long serialVersionUID = 1L; #JsonProperty("payment_details") private PaymentDetails payment_details; #JsonProperty("payment_details") public PaymentDetails getPayment_details() { return payment_details; } #JsonProperty("payment_details") public void setPayment_details(PaymentDetails payment_details) { this.payment_details = payment_details; } } ParentResponse model class extends ErrorResponse model class and this is how it looks like.. This ErrorResponse model represents above mentioned JSON. #JsonInclude(JsonInclude.Include.NON_NULL) public class ErrorResponse implements Serializable { private static final long serialVersionUID = 1L; #JsonProperty("statusCode") private int statusCode; #JsonProperty("error") private String error; #JsonProperty("message") private String message; #JsonProperty("err_data") private ErrData err_data; #JsonProperty("statusCode") public int getStatusCode() { return statusCode; } #JsonProperty("statusCode") public void setStatusCode(int statusCode) { this.statusCode = statusCode; } #JsonProperty("message") public String getMessage() { return message; } #JsonProperty("message") public void setMessage(String message) { this.message = message; } #JsonProperty("error") public String getError() { return error; } #JsonProperty("error") public void setError(String error) { this.error = error; } #JsonProperty("err_data") public ErrData getErr_data() { return err_data; } #JsonProperty("err_data") public void setErr_data(ErrData err_data) { this.err_data = err_data; } } err_data object is represented by below model class. #JsonInclude(JsonInclude.Include.ALWAYS) public class ErrData implements Serializable { private static final long serialVersionUID = 1L; #JsonProperty("email") private Email email; #JsonProperty("payment_details.type") private PaymentDetailsType payment_details_type; #JsonProperty("email") public Email getEmail() { return email; } #JsonProperty("email") public void setEmail(Email email) { this.email = email; } #JsonProperty("payment_details.type") public PaymentDetailsType getPayment_details_type() { return payment_details_type; } #JsonProperty("payment_details.type") public void setPayment_details_type(PaymentDetailsType payment_details_type) { this.payment_details_type = payment_details_type; } } payment_details.type object represented by below class. #JsonInclude(JsonInclude.Include.ALWAYS) public class PaymentDetailsType extends ErrorMessage implements Serializable { private static final long serialVersionUID = 1L; } #JsonInclude(JsonInclude.Include.ALWAYS) public class Email extends ErrorMessage implements Serializable { private static final long serialVersionUID = 1L; } And finally ErrorMessage which is extended by PaymentDetailsType as below. #JsonPropertyOrder({"location", "param", "value", "msg"}) public class ErrorMessage implements Serializable { private static final long serialVersionUID = 1L; #JsonProperty("location") private String location; #JsonProperty("param") private String param; #JsonProperty("value") private String value; #JsonProperty("msg") private String msg; #JsonProperty("location") public String getLocation() { return location; } #JsonProperty("location") public void setLocation(String location) { this.location = location; } #JsonProperty("param") public String getParam() { return param; } #JsonProperty("param") public void setParam(String param) { this.param = param; } #JsonProperty("value") public String getValue() { return value; } #JsonProperty("value") public void setValue(String value) { this.value = value; } #JsonProperty("msg") public String getMsg() { return msg; } #JsonProperty("msg") public void setMsg(String msg) { this.msg = msg; } } And finally I am trying to get "msg" field value as below - new Gson().fromJson(response.asString(), MyApiResponse.class).getErr_data().getPayment_details_type().getMsg(); I think there is something wrong with this one - Not sure how to define getter method if field name in json as . (dot). #JsonProperty("payment_details.type") public PaymentDetailsType getPayment_details_type() { return payment_details_type; } Similar to above, I am doing it for below json to retrieve "msg" value and it is working fine. { "statusCode": 422, "error": "Unprocessable Entity", "message": "Bad data received", "err_data": { "email": { "location": "body", "param": "email", "value": "test # com", "msg": "Must be a valid email" } } } This is returning correct "msg" value. new Gson().fromJson(response.asString(), MyApiResponse.class).getErr_data().getEmail().getMsg(); Please suggest! Thank you.
Here is a minimal example showing how to parse a JSON with Jackson, where the property names may contain dots: import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; class Main { public static void main(String[] args) throws IOException { String json = "{" + " \"payment_details.type\": {" + " \"location\": \"body\"" + " }" + "}"; ObjectMapper mapper = new ObjectMapper(); Response response = mapper.readValue(json, Response.class); System.out.println(response.getPaymentDetailsType().getLocation()); } } class Response { #JsonProperty("payment_details.type") private PaymentDetailsType paymentDetailsType; public PaymentDetailsType getPaymentDetailsType() { return paymentDetailsType; } } class PaymentDetailsType { private String location; public String getLocation() { return location; } } Note that you only need #JsonProperty when the expected property name in JSON cannot be deduced from a setter or variable name
Its also working with #JsonProperty. You just have to escape the dot: #JsonProperty("payment_details\\.type")
I think that most of my issues are related to an incorrect object map
I'm seeing weird inexplicable errors using Spring restTemplate and I think it might be related to an incorrect object map (I'm just learning). Here is the json from the head to the fields being mapped: { "response": { "version":"0.1", "termsofService":"http://www.wunderground.com/weather/api/d/terms.html", "features": { "conditions": 1 } } , "current_observation": { "image": { "url":"http://icons.wxug.com/graphics/wu2/logo_130x80.png", "title":"Weather Underground", "link":"http://www.wunderground.com" }, "display_location": { "full":"San Francisco, CA", "city":"San Francisco", "state":"CA", "state_name":"California", "country":"US", "country_iso3166":"US", "zip":"94101", "magic":"1", "wmo":"99999", "latitude":"37.77500916", "longitude":"-122.41825867", "elevation":"47.00000000" }, And here is my map: import com.fasterxml.jackson.annotation.JsonIgnoreProperties; #JsonIgnoreProperties(ignoreUnknown = true) public class Display { private String full; private String city; private String state; private String state_name; private String country; private String country_iso3166; private String zip; private String magic; private String who; private String lattitude; private String logitude; private String elevation; public String getFull() { return full; } public void setFull(String full) { this.full = full; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getState() { return state; } public void setState(String state) { this.state = state; } public String getState_name() { return state_name; } public void setState_name(String state_name) { this.state_name = state_name; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCountry_iso3166() { return country_iso3166; } public void setCountry_iso3166(String country_iso3166) { this.country_iso3166 = country_iso3166; } public String getZip() { return zip; } public void setZip(String zip) { this.zip = zip; } public String getMagic() { return magic; } public void setMagic(String magic) { this.magic = magic; } public String getWho() { return who; } public void setWho(String who) { this.who = who; } public String getLattitude() { return lattitude; } public void setLattitude(String lattitude) { this.lattitude = lattitude; } public String getLogitude() { return logitude; } public void setLogitude(String logitude) { this.logitude = logitude; } public String getElevation() { return elevation; } public void setElevation(String elevation) { this.elevation = elevation; } } Is there a problem with the way I've written the map? Thanks, Rob