How to send Date in REST API in POST method - java

I am trying to build RESTful web service with Spring support. I am getting following exception when I am trying to send POST request.
Input:
POST http://localhost:8080/InventoryDemo/item
In JSON Payload:
{"materialId":"ID02","materialName":"Material_2","materialCategory":"LIQUID","currency":"RUPEES","unitCostInCurrency":2200.0,"quantityLevel":1000,"quantityAtDate":"2016-04-11","warehouseName":"WareHouse_2"}
Exception:
WARNING: Handler execution resulted in exception: Could not read document: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2016-04-11'); no single-String constructor/factory method
at [Source: java.io.PushbackInputStream#378ace07; line: 1, column: 146] (through reference chain: com.psl.inventory.model.InventorySystemModel["quantityAtDate"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2016-04-11'); no single-String constructor/factory method
at [Source: java.io.PushbackInputStream#378ace07; line: 1, column: 146] (through reference chain: com.psl.inventory.model.InventorySystemModel["quantityAtDate"])
This is my POST method from #RestController:
#RequestMapping(value = "/item", method = RequestMethod.POST)
public ResponseEntity<Void> createInventorySystemModel(#RequestBody InventorySystemModel inventorySystemModel, UriComponentsBuilder ucBuilder) {
System.out.println("Creating InventorySystemModel " + inventorySystemModel.getMaterialName());
if (inventorySystemService.isInventorySystemModelExist(inventorySystemModel)) {
System.out.println("A InventorySystemModel with name " + inventorySystemModel.getMaterialName() + " already exist");
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
inventorySystemService.saveInventoryItem(inventorySystemModel);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/user/{materialId}").buildAndExpand(inventorySystemModel.getMaterialId()).toUri());
return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
}
and this is my POJO class:
public class InventorySystemModel {
private String materialId;
private String materialName;
private String materialCategory;
private String currency;
private double unitCostInCurrency;
private int quantityLevel;
private LocalDate quantityAtDate;
private String warehouseName;
public InventorySystemModel(){
}
public InventorySystemModel(String materialId, String materialName,
String materialCategory, String currency,
double unitCostInCurrency, int quantityLevel, LocalDate quantityAtDate,
String warehouseName) {
super();
this.materialId = materialId;
this.materialName = materialName;
this.materialCategory = materialCategory;
this.currency = currency;
this.unitCostInCurrency = unitCostInCurrency;
this.quantityLevel = quantityLevel;
this.quantityAtDate = quantityAtDate;
this.warehouseName = warehouseName;
}
public String getMaterialId() {
return materialId;
}
public void setMaterialId(String materialId) {
this.materialId = materialId;
}
public String getMaterialName() {
return materialName;
}
public void setMaterialName(String materialName) {
this.materialName = materialName;
}
public String getMaterialCategory() {
return materialCategory;
}
public void setMaterialCategory(String materialCategory) {
this.materialCategory = materialCategory;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public double getUnitCostInCurrency() {
return unitCostInCurrency;
}
public void setUnitCostInCurrency(double unitCostInCurrency) {
this.unitCostInCurrency = unitCostInCurrency;
}
public int getQuantityLevel() {
return quantityLevel;
}
public void setQuantityLevel(int quantityLevel) {
this.quantityLevel = quantityLevel;
}
public LocalDate getQuantityAtDate() {
return quantityAtDate;
}
public void setQuantityAtDate(LocalDate quantityAtDate) {
this.quantityAtDate = quantityAtDate;
}
public String getWarehouseName() {
return warehouseName;
}
public void setWarehouseName(String warehouseName) {
this.warehouseName = warehouseName;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((currency == null) ? 0 : currency.hashCode());
result = prime
* result
+ ((materialCategory == null) ? 0 : materialCategory.hashCode());
result = prime * result
+ ((materialId == null) ? 0 : materialId.hashCode());
result = prime * result
+ ((materialName == null) ? 0 : materialName.hashCode());
result = prime * result
+ ((quantityAtDate == null) ? 0 : quantityAtDate.hashCode());
result = prime * result + quantityLevel;
long temp;
temp = Double.doubleToLongBits(unitCostInCurrency);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result
+ ((warehouseName == null) ? 0 : warehouseName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InventorySystemModel other = (InventorySystemModel) obj;
if (currency == null) {
if (other.currency != null)
return false;
} else if (!currency.equals(other.currency))
return false;
if (materialCategory == null) {
if (other.materialCategory != null)
return false;
} else if (!materialCategory.equals(other.materialCategory))
return false;
if (materialId == null) {
if (other.materialId != null)
return false;
} else if (!materialId.equals(other.materialId))
return false;
if (materialName == null) {
if (other.materialName != null)
return false;
} else if (!materialName.equals(other.materialName))
return false;
if (quantityAtDate == null) {
if (other.quantityAtDate != null)
return false;
} else if (!quantityAtDate.equals(other.quantityAtDate))
return false;
if (quantityLevel != other.quantityLevel)
return false;
if (Double.doubleToLongBits(unitCostInCurrency) != Double
.doubleToLongBits(other.unitCostInCurrency))
return false;
if (warehouseName == null) {
if (other.warehouseName != null)
return false;
} else if (!warehouseName.equals(other.warehouseName))
return false;
return true;
}
#Override
public String toString() {
return "InventorySystemModel [materialId=" + materialId
+ ", materialName=" + materialName + ", materialCategory="
+ materialCategory + ", currency=" + currency
+ ", unitCostInCurrency=" + unitCostInCurrency
+ ", quantityLevel=" + quantityLevel + ", quantityAtDate="
+ quantityAtDate + ", warehouseName=" + warehouseName + "]";
}
}
FYI: I did checked this post but not getting clue like where exactly I need to do modification.
I am using Java 8 and Spring 4.2
Can some one please explain in detail like what exactly I need to do here.
Also I want same date format when I will hit GET request.
Thanks.

You can make your Custom LocalDate Deserializer. This Deserializer will be called when setter method for the LocalDate variable is called.
Steps as follows:
Define a Custom Deserializer
public class LocalDateDeserializer extends JsonDeserializer<LocalDate>{
#Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("required format");
LocalDate localDate = null;
localDate = LocalDate.parse(p.getText(), formatter);
return localDate;
}
}
Note: Reference for LocalDate.parse method.
Define #JsonDeserialize annotation above the variable
#JsonDeserialize(using=LocalDateDeserializer.class)
private LocalDate quantityAtDate;
For using #JsonDeserialize annotation import following:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
Hope this helps.

The error
JsonMappingException is an exception thrown by Jackson, a JSON parser for Java. It indicates fatal problems when mapping a JSON to a Java bean.
In this situation, looks like the string 2016-04-11 cannot be parsed to a LocalDate from Java 8.
How to fix it
Jackson supports Java 8 date types, but the following dependency is required:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
And then configure your ObjectMapper:
#Configuration
public class JacksonConfig {
#Bean
public ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
By default, dates will be serialized in the ISO 8601 format. If you want to change the format, you can use #JsonFormat:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM")
private LocalDate date;
No custom (de)serializers are required.

Make use of #JsonFormat to define the date format
http://www.baeldung.com/jackson-serialize-dates
public class InventorySystemModel {
private String materialId;
private String materialName;
private String materialCategory;
private String currency;
private double unitCostInCurrency;
private int quantityLevel;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date quantityAtDate;
private String warehouseName;
//getters and setters
}
Request :
{
"materialId": "ID02",
"materialName": "Material_2",
"materialCategory": "LIQUID",
"currency": "RUPEES",
"unitCostInCurrency": 2200.0,
"quantityLevel": 1000,
"quantityAtDate": "2016-04-11",
"warehouseName": "WareHouse_2"
}
Response :
InventorySystemModel [materialId=ID02, materialName=Material_2, materialCategory=LIQUID, currency=RUPEES, unitCostInCurrency=2200.0, quantityLevel=1000, quantityAtDate=Mon Apr 11 05:30:00 IST 2016, warehouseName=WareHouse_2]

Use a deserializer for parsing the LocalDate.
Add Maven dependency -
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.8.10</version>
</dependency>
If your restful service is parsing the bean directly, add the below
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
#PostMapping(value = "/xyz")
#JsonDeserialize(using = LocalDateDeserializer.class)
public ResponseEntity <String> testMethod (#RequestBody Bean bean){
}
Else, add deserializer in the bean class.

Related

Rest API ResponseEntity add extra attribute to response

I'm trying to figure out response of a GET method API, it is returning extra attribute with name "attributesMap"
Reponse entity return code is return ResponseEntity.ok(document);
document model class
private String id;
private String format;
private List<Attribute> attributes;
private String type;
private String accessright;
public Document() {
}
/**
* Copy constructor.
*/
public Document(Document source) {
this.id = source.id;
this.format = source.format;
this.type = source.type;
this.accessright = source.accessright;
this.attributes = source.attributes;
}
public static Document newDocument(String type, String id, String format, String accessright, List<Attribute> attributes) {
Document document = new Document();
document.type = type;
document.id = id;
document.format = format;
document.accessright = accessright;
document.attributes = attributes;
return document;
}
public static Document newCompleteDocument(String id, String type, String format, String accessright, List<Attribute> attributes) {
Document document = new Document();
document.id = id;
document.type = type;
document.format = format;
document.accessright = accessright;
document.attributes = attributes;
return document;
}
//
// Getters and setters ahead
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Attribute> getAttributes() {
return attributes;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
public String getAccessright() {
return accessright;
}
public void setAccessright(String accessright) {
this.accessright = accessright;
}
public Map<String, List<String>> getAttributesMap() {
Map<String, List<String>> attributesMap = getAttributes().stream().collect(Collectors.toMap(Attribute::getName, Attribute::getValues));
return attributesMap;
}
#Override
public String toString() {
final StringBuffer sb = new StringBuffer("Document{");
sb.append("id='").append(id).append('\'');
sb.append(", type='").append(type).append('\'');
sb.append(", format='").append(format).append('\'');
sb.append(", accessright='").append(accessright).append('\'');
sb.append(", attributes=[").append(attributes);
if (attributes != null) {
for (Attribute attribute:attributes) {
sb.append(attribute.getName()).append(":").append(attribute.getValues().toArray()).append(",");
}
}
sb.append("]}");
return sb.toString();
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Document document = (Document) o;
if (id != null ? !id.equals(document.id) : document.id != null) {
return false;
}
if (type != null ? !type.equals(document.type) : document.type != null) {
return false;
}
if (format != null ? !format.equals(document.format) : document.format != null) {
return false;
}
if (accessright != null ? !accessright.equals(document.accessright) : document.accessright != null) {
return false;
}
return attributes != null ? attributes.equals(document.attributes) : document.attributes == null;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (type != null ? type.hashCode() : 0);
result = 31 * result + (format != null ? format.hashCode() : 0);
result = 31 * result + (accessright != null ? accessright.hashCode() : 0);
result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
return result;
}
}
but the API reponse JSON as an extra attribute "attributesMap". Sample JSON as below:
{
"id": "xxx",
"format": "xx",
"attributes": [
{
"name": "attr1",
"values": [
"val1"
]
}
],
"type": "test type",
"accessright": "DELETE",
"attributesMap": {
"name": "attr1",
"values": [
"val1"
]
}
}
Can anyone help me figure out how to check where this attribute coming from , when I debugged till the return
return ResponseEntity.ok(document);
there is no attributesMap in the document model object.
The problem in your case is that you have the getter getAttributesMap. What will jackson serialize by default?
All public fields
All getter methods
What can you do?
Rename the method
Exclude the getter with #JsonIgnore
#JsonIgnore
public Map<String, List<String>> getAttributesMap() {
Map<String, List<String>> attributesMap = getAttributes().stream().collect(Collectors.toMap(Attribute::getName, Attribute::getValues));
return attributesMap;
}
You could also provide your own ObjectMapper bean with the following settings:
var mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
or
var mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
This would only serialize fields, not getters.

Problems with Spring Boot returning Map with own object as key

I have a problem with Spring Boot.
I am making a REST application, and I have a service that returns a Map(Share, Integer)
Share is a class written by me:
public class Share {
private String ticker;
private String name;
private Double value;
public Share() {
super();
}
public Share(String ticker, String name, Double value) {
super();
this.ticker = ticker;
this.name = name;
this.value = value;
}
public String getTicker() {
return ticker;
}
public void setTicker(String ticker) {
this.ticker = ticker;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((ticker == null) ? 0 : ticker.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Share other = (Share) obj;
if (ticker == null) {
if (other.ticker != null)
return false;
} else if (!ticker.equals(other.ticker))
return false;
return true;
}
#Override
public String toString() {
return "Share [ticker=" + ticker + ", name=" + name + ", value=" + value + "]";
}
}
And the #RestController is:
public class ShareController {
#Autowired
private ShareBussines shareBussines;
#RequestMapping("/getShare/{ticker}")
public Share getShare(#PathVariable("ticker") String ticker) throws BrokerNotFoundException, BrokerArgumentException, BrokerGeneralException {
return shareBussines.getShare(ticker);
}
#RequestMapping(value="/buyShares", method=RequestMethod.POST)
public Map<Share, Integer> buyShares(#RequestBody Map<String,Double> sharesToBuy) throws BrokerGeneralException, BrokerArgumentException, BrokerInsufficientStockException {
return shareBussines.buyShares(sharesToBuy);
}
}
The problem is when I call the service from Postman.
The result is:
{
"Share [ticker=AMZN, name=Amazon, value=259.32126508258295]": 1,
"Share [ticker=GOOGL, name=Google, value=249.35339337497606]": 1,
"Share [ticker=FB, name=Facebook, value=181.15005639608364]": 55
}
The Map key is share.toString()... I want the key to be the share JSON.
I try to remove the toString method from Share class, but the result was:
{
"Share#1eb87f": 1,
"Share#40d9fab": 1,
"Share#8db": 54
}
It is using the Object's toString().
Thank you for your advice.
First, it works as you coded it to work:
#RequestMapping(value="/buyShares", method=RequestMethod.POST)
public Map<Share, Integer> buyShares(#RequestBody Map<String,Double> sharesToBuy) throws BrokerGeneralException, BrokerArgumentException, BrokerInsufficientStockException {
return shareBussines.buyShares(sharesToBuy);
}
Share is a key here. And that is kinda weird. Why not create some object like:
public class ShareResponse {
private Share share;
private Integer someVal; // that's the one you have in your Map as a value
// getters and setters
}
And afterward change your service a bit:
#RequestMapping(value="/buyShares", method=RequestMethod.POST)
public List<ShareResponse> buyShares(#RequestBody Map<String,Double> sharesToBuy) throws BrokerGeneralException, BrokerArgumentException, BrokerInsufficientStockException {
// do your business here, create a list of ShareResponse and return it
return shareBussines.buyShares(sharesToBuy); // instead of this
}
And you should get a valid, nicely 'formatted' JSON. If you need each item to be identifiable by some unique value just add some ID field to ShareResponse.
Does it make any sense?)

JAVA DynamoDB: not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted

I've created CRUD methods but i have some problems with that.
This is my add method code:
public Product addProduct(Product content) {
Product item = new Product();
item.setName(content.getName());
item.setCalories(content.getCalories());
item.setFat(content.getFat());
item.setCarbo(content.getCarbo());
item.setProtein(content.getProtein());
item.setProductKinds(content.getProductKinds());
item.setAuthor(content.getAuthor());
item.setMedia(content.getMedia());
item.setApproved(content.getApproved());
databaseController.saveTest(item);
logger.log("Item created");
return item;
}
This is my editProduct method:
public Product editProduct(Product product) {
Product databaseProduct = databaseController.get(Product.class, product.getId());
databaseProduct.setAllProducts(product);
databaseController.save(databaseProduct);
return databaseProduct;
}
In model class I think that I have made everything right :
package pl.javamill.model.kitchen;
import com.amazonaws.services.dynamodbv2.datamodeling.*;
import pl.javamill.model.Request;
import pl.javamill.model.common.Author;
import pl.javamill.model.common.AuthorConverter;
import pl.javamill.model.common.Media;
import pl.javamill.model.common.MediaConverter;
import java.util.List;
#DynamoDBTable(tableName = "product")
public class Product extends Request {
/**
* Id of kitchen content
*/
private String id;
/**
* Name of product
*/
private String name;
/**
* Calories in 100g
*/
private Integer calories;
/**
* Fat in 100g
*/
private Double fat;
/**
* Total carbo in 100g
*/
private Double carbo;
/**
* Total Protein in 100g
*/
private Double protein;
/**
* Labels of product for example gluten fee product
*/
private List<ProductKind> productKinds;
/**
* Author of content.
*/
private Author author;
/**
* Address of content image.
*/
private Media media;
private Boolean approved;
#DynamoDBHashKey(attributeName = "id")
#DynamoDBAutoGeneratedKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#DynamoDBAttribute(attributeName = "Name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#DynamoDBAttribute(attributeName = "Calories")
public Integer getCalories() {
return calories;
}
public void setCalories(Integer calories) {
this.calories = calories;
}
#DynamoDBAttribute(attributeName = "Fat")
public Double getFat() {
return fat;
}
public void setFat(Double fat) {
this.fat = fat;
}
#DynamoDBAttribute(attributeName = "Carbo")
public Double getCarbo() {
return carbo;
}
public void setCarbo(Double carbo) {
this.carbo = carbo;
}
#DynamoDBAttribute(attributeName = "Protein")
public Double getProtein() {
return protein;
}
public void setProtein(Double protein) {
this.protein = protein;
}
#DynamoDBTypeConvertedEnum
#DynamoDBTypeConverted(converter = ProductKindConverter.class)
#DynamoDBAttribute(attributeName = "ProductKinds")
public List<ProductKind> getProductKinds() {
return productKinds;
}
public void setProductKinds(List<ProductKind> productKinds) {
this.productKinds = productKinds;
}
#DynamoDBTypeConverted(converter = AuthorConverter.class)
#DynamoDBAttribute(attributeName = "Author")
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
#DynamoDBTypeConverted(converter = MediaConverter.class)
#DynamoDBAttribute(attributeName = "Media")
public Media getMedia() {
return media;
}
public void setMedia(Media media) {
this.media = media;
}
#DynamoDBAttribute(attributeName = "Approved")
public Boolean getApproved() {
return approved;
}
public void setApproved(Boolean approved) {
this.approved = approved;
}
public void setAllProducts(Product product) {
if (!getName().equals(product.getName())) {
setName(product.getName());
}
if (!getCalories().equals(product.getCalories())) {
setCalories(product.getCalories());
}
if (!getFat().equals(product.getFat())) {
setFat(product.getFat());
}
if (!getCarbo().equals(product.getCarbo())) {
setCarbo(product.getCarbo());
}
if (!getProtein().equals(product.getProtein())) {
setProtein(product.getProtein());
}
if (!getProductKinds().equals(product.getProductKinds())) {
setProductKinds(product.getProductKinds());
}
if (!getAuthor().equals(product.getAuthor())) {
setAuthor(product.getAuthor());
}
if (!getMedia().equals(product.getMedia())) {
setMedia(product.getMedia());
}
if (!getApproved().equals(product.getApproved())) {
setApproved(product.getApproved());
}
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
if (name != null ? !name.equals(product.name) : product.name != null) return false;
if (calories != null ? !calories.equals(product.calories) : product.calories != null) return false;
if (fat != null ? !fat.equals(product.fat) : product.fat != null) return false;
if (carbo != null ? !carbo.equals(product.carbo) : product.carbo != null) return false;
if (protein != null ? !protein.equals(product.protein) : product.protein != null) return false;
if (productKinds != null ? !productKinds.equals(product.productKinds) : product.productKinds != null)
return false;
if (author != null ? !author.equals(product.author) : product.author != null) return false;
if (media != null ? !media.equals(product.media) : product.media != null) return false;
return approved != null ? approved.equals(product.approved) : product.approved == null;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (calories != null ? calories.hashCode() : 0);
result = 31 * result + (fat != null ? fat.hashCode() : 0);
result = 31 * result + (carbo != null ? carbo.hashCode() : 0);
result = 31 * result + (protein != null ? protein.hashCode() : 0);
result = 31 * result + (productKinds != null ? productKinds.hashCode() : 0);
result = 31 * result + (author != null ? author.hashCode() : 0);
result = 31 * result + (media != null ? media.hashCode() : 0);
result = 31 * result + (approved != null ? approved.hashCode() : 0);
return result;
}
}
ProductKindConventor:
public class ProductKindConverter implements DynamoDBTypeConverter<String, List<ProductKind>> {
#Override
public String convert(List<ProductKind> objects) {
//Jackson object mapper
ObjectMapper objectMapper = new ObjectMapper();
try {
String objectsString = objectMapper.writeValueAsString(objects);
return objectsString;
} catch (JsonProcessingException e) {
//do something
}
return null;
}
#Override
public List<ProductKind> unconvert(String objectsString) {
ObjectMapper objectMapper = new ObjectMapper();
try {
List<ProductKind> objects = objectMapper.readValue(objectsString, new TypeReference<List<ProductKind>>(){});
return objects;
} catch (JsonParseException e) {
//do something
} catch (JsonMappingException e) {
//do something
} catch (IOException e) {
//do something
}
return null;
}
}
Save method in dbcontroller:
public void saveTest(Product product){
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(regions).build();
mapper = new DynamoDBMapper(client);
mapper.save(product);
}
Get product from DB method:
public Product getTest(String id) {
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(regions).build();
mapper = new DynamoDBMapper(client);
Product retrivedProduct = mapper.load(Product.class, id);
return retrivedProduct;
}
Unit test passed, everything seems to be ok, but when I was using POSTMAN to test, i got some errors. I send product as json and it seems like that:
{"id":null,"name":"yoloornotyolo","calories":1000,"fat":400.0,"carbo":20.0,"protein":40.0,"productKinds":["MEAT"],"author":{"name":"Plejer Annołn","id":"testID2"},"media":{"name":"heheszki","url":"http://blabla.pl","mediaType":"IMAGE"},"approved":false}
Then I got ""message": "Internal server error"", so I check log file and that what I can see:
not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted:
com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException
I dont know what is wrong. Can someone explain me what I suppose to do?
It would be useful to see your saveProduct method. You are using DynamoDBMapper annotations but it looks like you are trying to save an item object, not a Product object. Rather than
Item item = new Item()
Use
Product item = new Product();
item.setName...
One thing I would mention is that you can offload the handling of your generated id to DyanmoDBMapper using the DynamoDBAutoGeneratedKey annotation
In your model class do this:
#DynamoDBHashKey(attributeName = "Id")
#DynamoDBAutoGeneratedKey
public String getId() { return id; }
public void setId(String id) { this.id = id; }
And then you dont need to handle id in your addProduct method
EDIT: Your saveProduct method should be along the lines of
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
mapper.save(item)
I doubt you need to be passing a tablename as that is an annotation on your Product object. You shouldn't be using putItem.
EDIT:
Your saveProduct method should take a Product object, not an item object. Dont use putItem because this doesn't know about your model class. Use DynamoDBMapper save function. You dont need to specify the table name as that's an annotation on your Product class (the table is called 'product'). You should use AmazonDynamoDB for interfacing to DynamoDB. The method below return is void, so you will need to update the addProduct method.
public void saveProduct(Product item) {
logger.log("Starting to save an item");
//You need a new method to return an AmazonDynamoDB interface
AmazonDynamoDB dynamoDB = getAmazonDynamoDB();
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDB);
mapper.save(item)
logger.log("Item stored");
return saved;
}
EDIT:
You build an AmazonDynamoDB interface like this
// Set to your chosen region
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.build();
EDIT:
DynamoDBMapper only supports certain data types.
You have three complex data types there; productKind, author and media. If you want to store these in your database, you will need to write a Convertor Class for each one. The convertor class typically define how you convert your object to a string and back again.
EDIT: Your set, ProductKind, will need a convertor that looks like this. Just change 'MyObject' to ProductKinds.
It would be useful to have a stack trace for the exception. In the intermediate message it contains the data type and field information which can not be converted.
Usually you do not need to make any convertors, just be sure to have #DynamoDBDocument on all custom classes, and #DynamoDBTypeConvertedEnum on all custom enums used.
I also had this error and it is a bit misleading, all we need to do is use #DynamoDBDocument on the class file of the referenced classes.
For your case, just put this annotation on ProductKind, like this:
#DynamoDBDocument
class ProductKind {
...
}
It should persist into dynamoDB as a map in the product table.
you will run into this error if the mapper class is
missing a No-Arg constructor
or has
invalid Setters/Getters
that do not comply with the camel case convention.
(Solved) Error: could not unconvert attribute dynamodb

Mapping a dynamic json object field in Jackson?

I have json objects in the following schema:
{
name: "foo",
timestamp: 1475840608763,
payload:
{
foo: "bar"
}
}
Here, the payload field contains an embedded json object, and the schema of this object is dynamic, and different each time.
The payload object is the raw output obtained from different API services, and different methods of different API services. It isn't possible to map it to all possible values.
Is it possible to have a java class such as the following:
public class Event
{
public String name;
public long timestamp;
public JsonObject payload;
}
Or something along those lines, so I can receive the basic schema and process it, then send it to the relevant class which will convert payload to its appropriate expected class?
Using JsonNode
You could use JsonNode from the com.fasterxml.jackson.databind package:
public class Event {
public String name;
public long timestamp;
public JsonNode payload;
// Getters and setters
}
Then parse it using:
String json = "{\"name\":\"foo\",\"timestamp\":1475840608763,"
+ "\"payload\":{\"foo\":\"bar\"}}";
ObjectMapper mapper = new ObjectMapper();
Event event = mapper.readValue(json, Event.class);
Mapping JsonNode to a POJO
Consider, for example, you want to map the JsonNode instance to the following class:
public class Payload {
private String foo;
// Getters and setters
}
It can be achieved with the following piece of code:
Payload payload = mapper.treeToValue(event.getPayload(), Payload.class);
Considering a Map<String, Object>
Depending on your requirements, you could use a Map<String, Object> instead of JsonNode:
public class Event {
public String name;
public long timestamp;
public Map<String, Object> payload;
// Getters and setters
}
If you need to convert a Map<String, Object> to a POJO, use:
Payload payload = mapper.convertValue(event.getPayload(), Payload.class);
According to the Jackson documentation, the convertValue() method is functionally similar to first serializing given value into JSON, and then binding JSON data into value of given type, but should be more efficient since full serialization does not (need to) occur. However, same converters (serializers and deserializers) will be used as for data binding, meaning same object mapper configuration works.
Does this help?
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
public class Payload {
private final Map<String, Object> other = new HashMap<>();
#JsonAnyGetter
public Map<String, Object> any() {
return other;
}
#JsonAnySetter
public void set(final String name, final Object value) {
other.put(name, value);
}
public Map<String, Object> getOther() {
return other;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((other == null) ? 0 : other.hashCode());
return result;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Payload)) {
return false;
}
Payload other = (Payload) obj;
if (this.other == null) {
if (other.other != null) {
return false;
}
} else if (!this.other.equals(other.other)) {
return false;
}
return true;
}
#Override
public String toString() {
return "Payload [other=" + other + "]";
}
}
Then this owning class
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Outer {
private final String name;
private final long timestamp;
private final Payload payload;
#JsonCreator
public Outer(#JsonProperty("name") final String name, #JsonProperty("timestamp") final long timestamp, #JsonProperty("payload") final Payload payload) {
super();
this.name = name;
this.timestamp = timestamp;
this.payload = payload;
}
public String getName() {
return name;
}
public long getTimestamp() {
return timestamp;
}
public Payload getPayload() {
return payload;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((name == null) ? 0 : name.hashCode());
result = (prime * result) + ((payload == null) ? 0 : payload.hashCode());
result = (prime * result) + (int) (timestamp ^ (timestamp >>> 32));
return result;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Outer)) {
return false;
}
Outer other = (Outer) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (payload == null) {
if (other.payload != null) {
return false;
}
} else if (!payload.equals(other.payload)) {
return false;
}
if (timestamp != other.timestamp) {
return false;
}
return true;
}
#Override
public String toString() {
return "Outer [name=" + name + ", timestamp=" + timestamp + ", payload=" + payload + "]";
}
}
Then to test
public class Main {
private static final ObjectMapper mapper = new ObjectMapper();
public static void main(final String... args) throws JsonParseException, JsonMappingException, IOException {
final Outer outer = mapper.readValue(new File("test.json"), Outer.class);
System.out.println(outer);
}
}
Gives console output of
Outer [name=foo, timestamp=1475840608763, payload=Payload [other={foo=bar}]]

Getting null value for xml formatted string after Deserialization

I am a beginner in android.I am calling a webservice from my android project which returns a json string as response which contain a xml formatted string as one entry.
String jsoncontent=restTemplate.getForObject(constr+"getAssetdata/{Id}", String.class, curAcct.getiD());
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Assets.class, new AssetDeserialiser());
final Gson gson = gsonBuilder.create();
Assets assetAcc = gson.fromJson(jsoncontent, Assets.class);
Toast.makeText(getApplicationContext(), assetAcc.getKeyValueData(), 68000).show();
Below is the json string that i got as webservice response
jsoncontent={"id":39,"name":"ICICI Bank","purchaseValue":6000.0,"purchaseDate":1402403751000,"keyValueData":"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><root><Description><Name>Tax and other payments</Name><Value>433</Value></Description><Description><Name>Add more details...</Name><Value></Value></Description></root>"}
But i am getting a null value for assetAcc.getKeyValueData() after deserialization,there is no isue with other fields in assets.How to solve this issue? Please help me.
AssetDeserialiser.java:
public class AssetDeserialiser implements JsonDeserializer<Assets> {
#Override
public Assets deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2) throws JsonParseException {
JsonObject jobject =arg0.getAsJsonObject();
final Assets asset = new Assets();
try{
asset.setId(jobject.get("id").getAsInt());
asset.setName(jobject.get("name").getAsString());
asset.setPurchaseValue(jobject.get("purchaseValue").getAsFloat());
asset.setPurchaseDate(new Timestamp(jobject.get("purchaseDate").getAsLong()));
asset.setKeyValueData(jobject.get("keyValueData").isJsonNull() ? "" : jobject.get("keyValueData").getAsString());
}catch(Exception es){
System.out.println("es "+es);
}
return asset;
}
}
Assets.java:
public class Assets implements Serializable{
private Integer id;
private String name;
private Float purchaseValue;
private Timestamp purchaseDate;
private String keyValueData;
public Assets() {
super();
// TODO Auto-generated constructor stub
}
public Assets(Integer id, String name, Float purchaseValue, Timestamp purchaseDate, String keyValueData) {
super();
this.id = id;
this.name = name;
this.purchaseValue = purchaseValue;
this.purchaseDate = purchaseDate;
this.keyValueData = keyValueData;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getPurchaseValue() {
return purchaseValue;
}
public void setPurchaseValue(Float purchaseValue) {
this.purchaseValue = purchaseValue;
}
public Timestamp getPurchaseDate() {
return purchaseDate;
}
public void setPurchaseDate(Timestamp purchaseDate) {
this.purchaseDate = purchaseDate;
}
public String getKeyValueData() {
return keyValueData;
}
public void setKeyValueData(String keyValueData) {
this.keyValueData = keyValueData;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result
+ ((keyValueData == null) ? 0 : keyValueData.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result
+ ((purchaseDate == null) ? 0 : purchaseDate.hashCode());
result = prime * result
+ ((purchaseValue == null) ? 0 : purchaseValue.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Assets other = (Assets) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (keyValueData == null) {
if (other.keyValueData != null)
return false;
} else if (!keyValueData.equals(other.keyValueData))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (purchaseDate == null) {
if (other.purchaseDate != null)
return false;
} else if (!purchaseDate.equals(other.purchaseDate))
return false;
if (purchaseValue == null) {
if (other.purchaseValue != null)
return false;
} else if (!purchaseValue.equals(other.purchaseValue))
return false;
return true;
}
#Override
public String toString() {
return name;
}
}
You can set this keyValueData after deserialisation from your json string that contain the xml string as below
String jsoncontent=restTemplate.getForObject(constr+"getAssetdata/{Id}", String.class, curAcct.getiD());
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Assets.class, new AssetDeserialiser());
final Gson gson = gsonBuilder.create();
Assets assetAcc = gson.fromJson(jsoncontent, Assets.class);
JSONObject jsonObj=new JSONObject(jsoncontent);
assetAcc.setKeyValueData(jsonObj.getString("keyValueData"));
1.Use Parcelable -its much faster.
2.Timestamp change to long. (Then can parce this value like this):
private String parceDate(data long){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm");
try {
retrun df.format(your long from Assets.class);
}catch (Exception e){
return "";
}
}
UPDATE:
Y can change your getter and setter for use Timestamp object from Assets class like this:
public void setPurchaseDate(long purchaseDate){
this.purchaseDate=purchaseDate
}
public Timestamp getPurchaseDate(){
return new Timestamp(purchaseDate); //from java.sql.Timestamp;
}
You can use jackson for deserialization.
public class AssetDeserialiser extends JsonDeserializer<Asset> {
#Override
public Asset deserialize(JsonParser arg0, DeserializationContext arg1)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(arg0);
final Asset asset = new Asset();
try{
asset.setId(mapper.readValue(node.get("id"),Integer.class));
asset.setName(mapper.readValue(node.get("name"),String.class));
asset.setPurchaseDate(mapper.readValue(node.get("purchaseDate"),Timestamp.class));
asset.setPurchaseValue(mapper.readValue(node.get("purchaseValue"),Float.class));
asset.setKeyValueData(mapper.readValue(node.get("keyValueData"), String.class));
}catch(Exception es){
System.out.println("es "+es);
}
return asset;
}
}
This may help you.
Also you will have to add "#JsonDeserialize(using=AssetDeserialiser.class)" at the beginning of your asset class. It is done like this:
#JsonDeserialize(using=AssetDeserialiser.class)
public class Asset implements Serializable{

Categories