i use jackson for jax-rs handler
the json member is always same, only 1 of it member have dynamic value ..
this dynamic value could only be "" or json object
json possibility 1
{
"event":"test",
"eventInfo": ""
}
json possibility 2
{
"event" : "test",
"eventInfo" : {
"name" : "abc",
"last" : "def"
}
}
eventInfo value could only be "" or json
i try to map this json to MyBean.java
MyBean.java
public class MyBean{
private String event;
private Map<String, String> eventInfo = new HashMap<String, String>();
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
#JsonAnyGetter
public Map getEventInfo() {
return eventInfo;
}
#JsonAnySetter
public void setEventInfo(String name, String value) {
this.eventInfo.put(name, value);
}
}
the mapping process happen in MyService.java
MyService.java
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces("text/plain")
public String receiveClientStatus(MyBean status){
if(!status.getEventInfo().isEmpty()){
String last = status.getEventInfo().get("last").toString() ;
System.err.println( last );
}
return "ok";
}
jackson fail convert the json as show above to MyBean.java
How to do this?
forgive my english
thanks
The issue is with the setEventInfo() method of MyBean object. In one case the value assigned to it is empty which is treated as string in other case its a linked hashmap. So modify the argument to accept Object. like
class MyBean {
private String event;
private Map<String, String> eventInfo = new HashMap<String, String>();
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
#JsonAnyGetter
public Map getEventInfo() {
return eventInfo;
}
#JsonAnySetter
public void setEventInfo(Object eventObject) {
if(eventObject instanceof Map){
this.eventInfo.putAll((Map<String, String>) eventObject);
}
}
#Override
public String toString() {
return "MyBean [event=" + event + ", eventInfo=" + eventInfo + "]";
}
}
Now it should work. Eg
String json1 = "{ \"event\":\"test\", \"eventInfo\": \"\" }";
String json2 = "{\"event\":\"test\",\"eventInfo\":{\"name\":\"abc\",\"last\":\"def\"}}";
ObjectMapper mapper = new ObjectMapper();
try {
MyBean bean1 = mapper.readValue(json1, MyBean.class);
System.out.println(bean1);
MyBean bean2 = mapper.readValue(json2, MyBean.class);
System.out.println(bean2);
} catch (IOException e) {
e.printStackTrace();
}
Related
I have below classes:
public class Result<T> {
public int code;
public Object meta;
public T data;
}
public class User {
public int id;
public String name;
}
public class Error {
public String field;
public String message;
}
I want to deserialize a JSON payload based on code field. If code >= 10, return Result<ArrayList<Error>>, otherwise return Result<User>
Currently, I map JSON to Result<Object> first, then check the code field. Based on that value I make second map to desired object.
ObjectMapper mapper = new ObjectMapper();
Result<Object> tempResult = mapper.readValue(json, new TypeReference<Result<Object>>() {});
if (tempResult.code < 10) {
Result<User> result = mapper.readValue(json, new TypeReference<Result<User>>() {});
return result;
} else {
Result<ArrayList<Error>> result = mapper.readValue(json, new TypeReference<Result<ArrayList<Error>>>() {});
return result;
}
Is there an elegant way to do this without deserializing it 2 times?
You need to implement custom TypeIdResolver:
class UserTypeIdResolverBase extends TypeIdResolverBase {
#Override
public String idFromValue(Object value) {
throw new IllegalStateException("Not implemented!");
}
#Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
throw new IllegalStateException("Not implemented!");
}
#Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.CUSTOM;
}
#Override
public JavaType typeFromId(DatabindContext context, String id) {
if (Integer.parseInt(id) < 10) {
return context.getTypeFactory().constructType(new TypeReference<Result<User>>() {});
}
return context.getTypeFactory().constructType(new TypeReference<Result<List<Error>>>() {});
}
}
and declare it for a Result class:
#JsonTypeInfo(property = "code", use = JsonTypeInfo.Id.CUSTOM, visible = true)
#JsonTypeIdResolver(UserTypeIdResolverBase.class)
class Result<T>
I have a JSON String as below:
"{ \"password\":\"des123\",\"ROU_DATA\":[{\"FORM_RECEIVING_TIME\":\"12:00:00\",\"REMARKS\":\"Redemption of Unit\"}, {\"FORM_RECEIVING_TIME\":\"13:00:00\",\"REMARKS\":\"sALE of Unit\"}] }";
Now I want to extract the Array from it and need to use it as a separate pojo class so that I can iterate over each value..
Now the problem is, when I try to convert the complete String to Map and get the Array value from the map.. It transforms its format to MAp format like:
{FORM_RECEIVING_DATE = 12:00:00, etc..}
However json string should be {"FORM_RECEIVING_DATE": "12:00:00", etc..}
due to the MAp format its now allowing me to parse it using my POJO Class..
Please help to convert it to my JSONFormat ...
**NOTE: Please note that I can only use Jackson **.
CLASS A
ObjectMapper mapper2 = new ObjectMapper();
Map<String, Object> map;
map = mapper2.readValue(json, new TypeReference<Map<String, Object>>(){});
System.out.println("map: " + map.get("ROU_DATA") );
String array = map.get("ROU_DATA").toString();
String json2 = new ObjectMapper().writeValueAsString(array.replace("[", "").replace("]", ""));
String json3 = new ObjectMapper().writeValueAsString(json2);
System.out.println("json2>>" + json2);
System.out.println("json2>>" + json3);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 1. convert JSON array to Array objects
ROU[] pp1 = mapper.readValue("{" + array.replace("=", ":") + "}", ROU[].class);
for (ROU person : pp1) {
System.out.println(person.getRemarks());
}
CLASS B
import com.fasterxml.jackson.annotation.JsonProperty;
public class ROU {
#JsonProperty("FORM_RECEIVING_TIME")
private String formdate;
#JsonProperty("REMARKS")
private String remarks;
public String getFormdate() {
return formdate;
}
public void setFormdate(String formdate) {
this.formdate = formdate;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
map.get("ROU_DATA") returns a List object, and the toString() method of List does not generate JSON text.
You don't need to convert back to a JSON text just to get the ROU[] created, just call convertValue(...).
String input = "{ \"password\":\"des123\",\"ROU_DATA\":[{\"FORM_RECEIVING_TIME\":\"12:00:00\",\"REMARKS\":\"Redemption of Unit\"}, {\"FORM_RECEIVING_TIME\":\"13:00:00\",\"REMARKS\":\"sALE of Unit\"}] }";
ObjectMapper mapper2 = new ObjectMapper();
Map<?, ?> json = mapper2.readValue(input, Map.class);
ROU[] pp1 = mapper2.convertValue(json.get("ROU_DATA"), ROU[].class);
for (ROU person : pp1)
System.out.println(person.getRemarks());
Output
Redemption of Unit
sALE of Unit
class A
public class ROU {
#JsonProperty("FORM_RECEIVING_TIME")
private String formdate;
#JsonProperty("REMARKS")
private String remarks;
public String getFormdate() {
return formdate;
}
public void setFormdate(String formdate) {
this.formdate = formdate;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
class B
public class ObjOuter {
private String password;
#JsonProperty("ROU_DATA")
private List<ROU> rous;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<ROU> getRous() {
return rous;
}
public void setRous(List<ROU> rous) {
this.rous = rous;
}
}
json to Object
ObjectMapper mapper = new ObjectMapper();
try {
ObjOuter outer = mapper.readValue(str, ObjOuter.class);
for (ROU rou : outer.getRous()) {
System.out.println(rou.getFormdate());
System.out.println(rou.getRemarks());
}
} catch (IOException e) {
e.printStackTrace();
}
I've got a JSON input like this
{
"slices": [{
"slice": {
"boundedBy": {
"Envelope": {
"axisLabels": "Lat Long ansi",
"lowerCorner": "-44.975 111.975 \"2003-01-01T00:00:00+00:00\"",
"upperCorner": "-8.975 155.975 \"2003-01-01T00:00:00+00:00\"",
"srsDimension": 3
}
},
"fileReferenceHistory": "/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20030101-20030110-H01V06-1.0_MERIS-FR-LAI-HA.tiff",
"local_metadata_key": "value_1"
}
},
{
"slice": {
"boundedBy": {
"Envelope": {
"axisLabels": "Lat Long ansi",
"lowerCorner": "-44.975 111.975 \"2003-10-01T00:00:00+00:00\"",
"upperCorner": "-8.975 155.975 \"2003-10-01T00:00:00+00:00\"",
"srsDimension": 3
}
},
"fileReferenceHistory": "/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20031001-20031010-H00V10-1.0_MERIS-FR-LAI-HA.tiff",
"local_metadata_key": "value_2"
}
}
],
"Title": "Drought code",
// other keys:values
}
with "slices" is an array of "slice" objects. Out of "slices" is any "keys":"values" but it is not the problem.
Then, I have a POJO class
public class CoverageMetadata {
#JsonProperty(value = "slices")
#JacksonXmlElementWrapper(useWrapping = false)
private List<LocalMetadata> localMetadataList;
private Map<String, String> globalMetadataAttributesMap;
#JsonAnySetter
public void addKeyValue(String key, String value) {
this.globalMetadataAttributesMap.put(key, value);
}
#JsonAnyGetter
public Map<String, String> getGlobalAttributesMap() {
return globalMetadataAttributesMap;
}
// other gettters, setters without Jackson annotations
}
and a class inside the list:
public class LocalMetadata {
public static final String LOCAL_METADATA_TAG = "slice";
private Map<String, String> localMetadataAttributesMap;
private BoundedBy boundedBy;
#JsonAnySetter
// NOTE: To map an unknown list of properties, must use this annotation
public void addKeyValue(String key, String value) {
this.localMetadataAttributesMap.put(key, value);
}
public LocalMetadata() {
this.localMetadataAttributesMap = new LinkedHashMap<>();
this.boundedBy = new BoundedBy();
}
#JsonAnyGetter
// NOTE: to unwrap the "map" from { "map": { "key": "value" } }, only keep { "key": "value" }
public Map<String, String> getLocalMetadataAttributesMap() {
return localMetadataAttributesMap;
}
public BoundedBy getBoundedBy() {
return this.boundedBy;
}
public void setBoundedBy(BoundedBy boundedBy) {
this.boundedBy = boundedBy;
}
public LocalMetadata(Map<String, String> localMetadataAttributesMap, BoundedBy boundedBy) {
this.localMetadataAttributesMap = localMetadataAttributesMap;
this.boundedBy = boundedBy;
}
}
And the basic code to deserialize JSON to object
ObjectMapper objectMapper = new ObjectMapper();
CoveageMetadata coverageMetadata = objectMapper.readValue(metadata, CoverageMetadata.class);
When I try to deserialize the JSON input to CoverageMetadata object, I got the error
Cannot deserialize coverage's metadata in XML/JSON by Jackson, error: Can not deserialize instance of java.lang.String out of START_OBJECT token
at [Source: {"slices":[{"slice":{"boundedBy":{"Envelope":{"axisLabels":"Lat Long ansi","srsDimension":3,"lowerCorner":"-44.975 111.975 \"2003-01-01T00:00:00+00:00\"","upperCorner":"-8.975 155.975 \"2003-01-01T00:00:00+00:00\""}},"local_metadata_key":"value_1","fileReferenceHistory":"/home/rasdaman/rasdaman_community/rasdaman/systemtest/testcases_services/test_all_wcst_import/testdata/wcs_local_metadata_tiff_no_specify_bands/GlobLAI-20030101-20030110-H01V06-1.0_MERIS-FR-LAI-HA.tiff"}}],"Title":"Drought code"}; line: 1, column: 21] (through reference chain: petascope.core.gml.metadata.model.CoverageMetadata["slices"]->java.util.ArrayList[0]->petascope.core.gml.metadata.model.LocalMetadata["slice"]).
How can I deserialize this JSON input String to CoverageMetadataObject with each "slice" element will be mapped to a LocalMetadata object?
The simple answer is I create another POJO class to hold the "slices" list, in CoverageMetadata class, it will have
public class CoverageMetadata {
private Map<String, String> globalMetadataAttributesMap;
#JsonProperty(value = "slices")
private LocalMetadata localMetadata;
...
}
New POJO class (class LocalMetadata before was renamed to LocalMetadataChild)
public class LocalMetadata {
#JsonProperty(value = "slice")
// This is the most important thing to avoid duplicate <slices><slices> when serializing in XML.
#JacksonXmlElementWrapper(useWrapping = false)
private List<LocalMetadataChild> localMetadataList;
public LocalMetadata(List<LocalMetadataChild> localMetadataList) {
this.localMetadataList = localMetadataList;
}
public LocalMetadata() {
this.localMetadataList = new ArrayList<>();
}
public List<LocalMetadataChild> getLocalMetadataList() {
return localMetadataList;
}
public void setLocalMetadataList(List<LocalMetadataChild> localMetadataList) {
this.localMetadataList = localMetadataList;
}
}
I'm new to Jackson. I've tried to parse Json string to an object but jackson returns an object with all null values. Here is code of my parser:
ObjectMapper mapper = new ObjectMapper();
FullTextRetrievalResponse object =
mapper.readValue(response.getBody().getObject().toString(),
FullTextRetrievalResponse.class);
Here is my FullTextRetrievalResponse class:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"coredata",
"scopus-id",
"scopus-eid",
"link",
"originalText"
})
public class FullTextRetrievalResponse {
#JsonProperty("coredata")
private Coredata coredata;
#JsonProperty("scopus-id")
private String scopusId;
#JsonProperty("scopus-eid")
private String scopusEid;
#JsonProperty("link")
private Link_ link;
#JsonProperty("originalText")
private OriginalText originalText;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("coredata")
public Coredata getCoredata() {
return coredata;
}
#JsonProperty("coredata")
public void setCoredata(Coredata coredata) {
this.coredata = coredata;
}
#JsonProperty("scopus-id")
public String getScopusId() {
return scopusId;
}
#JsonProperty("scopus-id")
public void setScopusId(String scopusId) {
this.scopusId = scopusId;
}
#JsonProperty("scopus-eid")
public String getScopusEid() {
return scopusEid;
}
#JsonProperty("scopus-eid")
public void setScopusEid(String scopusEid) {
this.scopusEid = scopusEid;
}
#JsonProperty("link")
public Link_ getLink() {
return link;
}
#JsonProperty("link")
public void setLink(Link_ link) {
this.link = link;
}
#JsonProperty("originalText")
public OriginalText getOriginalText() {
return originalText;
}
#JsonProperty("originalText")
public void setOriginalText(OriginalText originalText) {
this.originalText = originalText;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
and here is part of JSON:
{
"full-text-retrieval-response": {
"coredata": {
"prism:url": "http://api.elsevier.com/content/article/pii/S1751157716302140",
"dc:identifier": "doi:10.1016/j.joi.2016.11.002",
"eid": "1-s2.0-S1751157716302140",
"prism:doi": "10.1016/j.joi.2016.11.002",
"pii": "S1751-1577(16)30214-0",
"dc:title": "The specific shapes of gender imbalance in scientific authorships: A network approach ",
"prism:publicationName": "Journal of Informetrics",
"prism:aggregationType": "Journal",
"prism:issn": "17511577",
"prism:coverDate": "2017-02-28",
"prism:coverDisplayDate": "February 2017",
"openaccess": "0",
"openaccessArticle": false,
"openaccessType": null,
"openArchiveArticle": false,
"openaccessSponsorName": null,
"openaccessSponsorType": null,
"openaccessUserLicense": null,
"link": [
{
"#rel": "self",
"#href": "http://api.elsevier.com/content/article/pii/S1751157716302140",
"#_fa": "true"
},
{
"#rel": "scidir",
"#href": "http://www.sciencedirect.com/science/article/pii/S1751157716302140",
"#_fa": "true"
}
]
}
}
}
The issue is that in your json object you have the field full-text-retrieval-response wrapping all your object, but in your java classes, the FullTextRetrievalResponse is the root.
I think you have 3 options
Change the json structure (supposing you can do that), removing the full-text-retrieval-response label (https://pastebin.com/MtxXSeDW)
Create a new class having an instance of FullTextRetrievalResponse as a json property:
public class FullTextRetrievalResponseWrapper {
#JsonProperty("full-text-retrieval-response")
private FullTextRetrievalResponse fullTextRetrievalResponse;
//setters and getters
}
And then make the serialization using this new class: mapper.readValue(response.getBody().getObject().toString(),
FullTextRetrievalResponseWrapper .class);
Create a custon json deserializer (http://www.baeldung.com/jackson-deserialization) to convert yourself the json object to your class.
Just another quick tip: if you are defining a field as a json property (#JsonProperty), you do not need to define the #JsonSetter, #JsonGetter or even the #JsonProperty in the setters and getters.
I am trying to parse the following JSON object:
{
"key1": "value1",
"key2": "value2",
"key3": {
"subkey1": {
"subsubkey1": "value",
"subsubkey2": "value"
},
"subkey2": {
"subsubkey1": "value",
"subsubkey2": "value"
}
...........other dynamic subkeys..............
}
}
I tried the following:
public class MyObject{
String key1, key2;
KEY3 key3;
public class KEY3{
public class SUBKEY{
String subsubkey1;
String subsubkey2;
//getters and setters
}
}
//getters and setters
}
and then did the following:
MyObject mObject = gson.fromJson(jsonMessage, MyObject.class);
where jsonMessage is the JSON string above and subkeys are dynamic so i do not know how many of them are there.. But key3 becomes null. So, my problem is, how can I get key3 and its subkeys and subvalues using gson.fromJson? I do not want to do it like the following:
JSONObject jObject= new JSONObject(jsonMessage);
JSONObject key3Object = jObject.getJsonObject("key3");
I want to use gson.fromJson(); directly.
You can try with three different POJO classes that is exact replica of this JSON string.
class MyObject {
private String key1, key2;
private KEY3 key3;
}
class KEY3 {
private SUBKEY3 subkey1;
private SUBKEY3 subkey2;
// getters and setters
}
class SUBKEY3 {
private String subsubkey1;
private String subsubkey2;
}
...
MyObject data = new Gson().fromJson(jsonString, MyObject.class);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));
output:
{
"key1": "value1",
"key2": "value2",
"key3": {
"subkey1": {
"subsubkey1": "value",
"subsubkey2": "value"
},
"subkey2": {
"subsubkey1": "value",
"subsubkey2": "value"
}
}
}
If keys are dynamic and JSON string is not known in advance then try with Map<String,Object> using TypeToken
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));
You need two instance variables in KEY3 called subkey1 and subkey2 of type SUBKEY.
It should work fine. Provided the key3 class should have fields subKey1 & subKey2. Eg
import java.util.Map;
import com.google.gson.Gson;
class MyObject {
String key1, key2;
Map<String, SUBKEY> key3;
public class SUBKEY {
String subsubkey1;
String subsubkey2;
public String getSubsubkey1() {
return subsubkey1;
}
public void setSubsubkey1(String subsubkey1) {
this.subsubkey1 = subsubkey1;
}
public String getSubsubkey2() {
return subsubkey2;
}
public void setSubsubkey2(String subsubkey2) {
this.subsubkey2 = subsubkey2;
}
#Override
public String toString() {
return "SUBKEY [subsubkey1=" + subsubkey1 + ", subsubkey2="
+ subsubkey2 + "]";
}
}
public String getKey1() {
return key1;
}
public void setKey1(String key1) {
this.key1 = key1;
}
public String getKey2() {
return key2;
}
public void setKey2(String key2) {
this.key2 = key2;
}
public Map<String, SUBKEY> getKey3() {
return key3;
}
public void setKey3(Map<String, SUBKEY> key3) {
this.key3 = key3;
}
#Override
public String toString() {
return "MyObject [key1=" + key1 + ", key2=" + key2 + ", key3=" + key3
+ "]";
}
}
public class Sample {
public static void main(String[] args) {
String jsonMessage = "{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":{\"subkey1\":{\"subsubkey1\":\"value\",\"subsubkey2\":\"value\"},\"subkey2\":{\"subsubkey1\":\"value\",\"subsubkey2\":\"value\"}}}";
Gson gson = new Gson();
MyObject mObject = gson.fromJson(jsonMessage, MyObject.class);
System.out.println(mObject);
}
}
You're just confused with nested classes and inner classes. The inner class (without static in definition) is some class, which instances always have "parent" instance. As for nested classes (with static), they're independent on the "parent" class and that's what we need in the case. So, the solution would be so:
public class MyObject {
String key1, key2;
Key3 key3;
public static class Key3 {
public static class Subkey {
String subsubkey1;
String subsubkey2;
//getters and setters
}
}
//getters and setters
}
For the better clarification, you should look at this thread: http://code.google.com/p/google-gson/issues/detail?id=135