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}]]
Related
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?)
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.
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{
I have a java bean class 'MenuItem' which consist of list of children .
I want to display iterate and render through this object in jsp and display the menu tree.
I tried the json data creation, but it needs ajax call. And in my case, I need to submit the page instead of ajax.
I am using struts 2. Can anyone please suggest how can I render and iterate through my bean object in jsp ?
Thanks in Advance.
Here is my Menu bean object:
public class MenuItem implements Serializable {
private static final long serialVersionUID = 8690828081102943225L;
private Long id;
private String name;
private Collection<MenuItem> children;
private String url;
private String actionHoverLabel;
private String actionLabel;
private Long displayOrder;
private Boolean isLeaf;
private Boolean isVisible;
private Long menuLevel;
private Long parentId;
public String getActionHoverLabel() {
return actionHoverLabel;
}
public void setActionHoverLabel(String actionHoverLabel) {
this.actionHoverLabel = actionHoverLabel;
}
public String getActionLabel() {
return actionLabel;
}
public void setActionLabel(String actionLabel) {
this.actionLabel = actionLabel;
}
public Long getDisplayOrder() {
return displayOrder;
}
public void setDisplayOrder(Long displayOrder) {
this.displayOrder = displayOrder;
}
public Boolean getIsLeaf() {
return isLeaf;
}
public void setIsLeaf(Boolean isLeaf) {
this.isLeaf = isLeaf;
}
public Boolean getIsVisible() {
return isVisible;
}
public void setIsVisible(Boolean isVisible) {
this.isVisible = isVisible;
}
public Long getMenuLevel() {
return menuLevel;
}
public void setMenuLevel(Long menuLevel) {
this.menuLevel = menuLevel;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public MenuItem() {
children = new ArrayList<MenuItem>();
isVisible = true;
}
public MenuItem(Long id, String name) {
this.id = id;
this.name = name;
children = new ArrayList<MenuItem>();
isVisible = true;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Collection<MenuItem> getChildren() {
return this.children;
}
public void setChildren(Collection<MenuItem> children) {
this.children = children;
}
public boolean isLeaf() {
if (children != null && children.size() > 0) {
return false;
} else {
return true;
}
}
public void addChild(MenuItem child) {
if (child != null && children != null) {
children.add(child);
child.setParentId(this.getId());
}
}
public void removeChild(MenuItem child) {
if (child != null && children != null) {
children.remove(child);
child.setParentId(null);
}
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
if (url == null) {
return name;
} else {
return url;
}
}
#Override
public boolean equals(Object obj) {
MenuItem m = null;
boolean isEqual = false;
if (id != null && id >= 0) {
m = (MenuItem) obj;
if (m.getId().equals(id)
isEqual = true;
} else {
isEqual = super.equals(obj);
}
return isEqual;
}
#Override
public int hashCode() {
if (id != null && id >= 0) {
return id.hashCode();
} else {
return super.hashCode();
}
}
#Override
public String toString() {
return "name: " + name + " id: " + id;
}
If you want to operate on the bean in JSP, you can very well do that with struts tags. The bean has to be in the scope.
Why do you need JSON in this case?
If you still want JSON, the check for GSON library and Jackson mapper library.
On the JSP, you can store the JSON object in a variable and then operate on that.
On page submit Struts action would form the JSON and then set it in a page bean variable.
Go to www.json.org
Download Java Library for JSON encoding.
Compile it (build a jar for convenience).
Write a method alike this to JSON Serialize your bean:
public static String toJson(SomeBean bean)
{
JSONObject jo = new JSONObject(bean);
return jo.toString();
}
Deserialization is a little tricky but should work like:
JSONObject jo = new JSONObject(jsonString);
SomeBean res = new SomeBean();
res.someProperty = jo.getString("someProperty");
res.someIntProperty= jo.getInt("someIntProperty");
Obviously you have to take care of complex properties, but for simple beans it should work out like a charm.
Straight from sources:
/**
* Construct a JSONObject from an Object using bean getters.
* It reflects on all of the public methods of the object.
* For each of the methods with no parameters and a name starting
* with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
* the method is invoked, and a key and the value returned from the getter method
* are put into the new JSONObject.
*
* The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
* If the second remaining character is not upper case, then the first
* character is converted to lower case.
*
* For example, if an object has a method named <code>"getName"</code>, and
* if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
* then the JSONObject will contain <code>"name": "Larry Fine"</code>.
*
* #param bean An object that has getter methods that should be used
* to make a JSONObject.
*/
public JSONObject(Object bean) {
this();
this.populateMap(bean);
}
Are you sure you've everything set up correctly (Classpath) ?
Here is my dilemma:
I have a dto class for marshaling back and forth from/to XML.
Here is the trick: Because of the number of dto classes our project deals with that are collections with a plural outter tag, I decided to create a delegate collection that allows me to take one of these classes and effortlessly turn them into a Collection and get the convenience that comes with it (iteration, add, etc.).
In our project we have marshaling tests to flush out annotation errors and such.
Below is my trouble code.
Problem:
Depending on the marshaler, if I extend this QuickCollection I get the below error.
When the object is unmarshaled to xml using CXF as a response to a webservice request, it fails. Exact error:
com.sun.istack.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation
When it's marshaled/unmarshaled with JAXB in test it's fine.
When This same QuickCollection is used to marshal in results from 3rd parties using spring RestOperations and works fine
the mind screw:
When I remove the inheritance and manage the collection as a private member it all just works!
This makes not a stitch of sense to me as I am literally returning the exact data type in both situations.
Below is all relevant code.
This is the Inherited delegate class.
public class QuickCollection<T> implements Collection<T> {
// to be set if needed after instantiation. To behave like a normal collection, we set it to something safe
protected Collection<T> delegate = Collections.emptySet();
public QuickCollection() {
}
public QuickCollection(Collection<T> delegate) {
this.delegate = delegate;
}
#Override
public int size() {
return delegate.size();
}
#Override
public boolean isEmpty() {
return delegate.isEmpty();
}
#Override
public boolean contains(Object o) {
return delegate.contains(o);
}
#Override
public Iterator<T> iterator() {
return delegate.iterator();
}
#Override
public Object[] toArray() {
return delegate.toArray();
}
#Override
public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}
#Override
public boolean add(T t) {
return delegate.add(t);
}
#Override
public boolean remove(Object o) {
return delegate.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
return delegate.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends T> c) {
return delegate.addAll(c);
}
#Override
public boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}
#Override
public void clear() {
delegate.clear();
}
#Override
public String toString() {
return "" + delegate.toString();
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
QuickCollection that = (QuickCollection) o;
if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false;
return true;
}
#Override
public int hashCode() {
return delegate != null ? delegate.hashCode() : 0;
}
}
Here is the child DTO class
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name = "BuddyCodes")
#XmlRootElement(name = "BuddyCodes")
public class BuddyCodes extends QuickCollection<String> implements Xml {
private Long accountId;
private Date expirationDate;
public BuddyCodes() {
super.delegate = new HashSet<String>();
}
public BuddyCodes(Long accountId, Set<String> codes, Date expirationDate) {
super(codes);
this.accountId = accountId;
this.expirationDate = expirationDate;
super.delegate = new HashSet<String>();
}
public BuddyCodes(Long accountId, Date expirationDate) {
this.accountId = accountId;
this.expirationDate = expirationDate;
super.delegate = new HashSet<String>();
}
#Override
public String toXml() {
String retVal;
try {
retVal = StringUtils.toXml(this);
}
catch (JAXBException e) {
retVal = e.toString();
}
return retVal;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public Set<String> getCodes() {
return (Set<String>) super.delegate;
}
#XmlElement(name = "code")
public void setCodes(Set<String> codes) {
super.delegate = codes;
}
public Date getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BuddyCodes that = (BuddyCodes) o;
if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false;
if (delegate != null ? !super.delegate.equals(that.delegate) : that.delegate != null) return false;
if (expirationDate != null ? !expirationDate.equals(that.expirationDate) : that.expirationDate != null)
return false;
return true;
}
#Override
public int hashCode() {
int result = accountId != null ? accountId.hashCode() : 0;
result = 31 * result + (expirationDate != null ? expirationDate.hashCode() : 0);
result = 31 * result + (super.delegate != null ? super.delegate.hashCode() : 0);
return result;
}
#Override
public String toString() {
return "BuddyCodes{" +
"accountId=" + accountId +
"codes=" + super.delegate +
", expirationDate=" + expirationDate +
'}';
}
}
And it doesn't work. I get the error.
Now, here is the child class after removing the inheritance and it works!!!
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.*;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
/**
* #author christian.bongiorno
* Date: 10/3/11
* Time: 6:11 PM
*/
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name = "BuddyCodes")
#XmlRootElement(name = "BuddyCodes")
public class BuddyCodes implements Xml {
private Long accountId;
private Date expirationDate;
private Set<String> delegate;
public BuddyCodes() {
delegate = new HashSet<String>();
}
public BuddyCodes(Long accountId, Set<String> codes, Date expirationDate) {
this.accountId = accountId;
this.expirationDate = expirationDate;
delegate = new HashSet<String>();
}
public BuddyCodes(Long accountId, Date expirationDate) {
this.accountId = accountId;
this.expirationDate = expirationDate;
delegate = new HashSet<String>();
}
#Override
public String toXml() {
String retVal;
try {
retVal = StringUtils.toXml(this);
}
catch (JAXBException e) {
retVal = e.toString();
}
return retVal;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public Set<String> getCodes() {
return delegate;
}
#XmlElement(name = "code")
public void setCodes(Set<String> codes) {
delegate = codes;
}
public Date getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(Date expirationDate) {
this.expirationDate = expirationDate;
}
public boolean add(String s) {
return delegate.add(s);
}
public int size() {
return delegate.size();
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BuddyCodes that = (BuddyCodes) o;
if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false;
if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false;
if (expirationDate != null ? !expirationDate.equals(that.expirationDate) : that.expirationDate != null)
return false;
return true;
}
#Override
public int hashCode() {
int result = accountId != null ? accountId.hashCode() : 0;
result = 31 * result + (expirationDate != null ? expirationDate.hashCode() : 0);
result = 31 * result + (delegate != null ? delegate.hashCode() : 0);
return result;
}
}
Why does the inheritance matter at all???
I haven't figured this out but, I have another DTO in a similar layout (BuddyTypes BuddyType). BuddyType has 2 members: Long and String. Both are annoted as XmlElement. This one works just fine.
It seems the problem that the members of the set making up the delegate are not annotated in my problem case and I don't know how to annotate a parent member. As an inherited class, it wouldn't make sense to have some sort of default name/annotation. But, I tried this madness and the annotation is ignored -- I have seen parent member annotations ignored before so this isn't new.
I don't know if it's possible, but I need to annotate a parent member.
A bit out of the box: try Simple XML library instead of JAXB. My experience with it is the best.