I want to serialize a Map<String,Object> to JSON with Jackson and back. The object type can be of several beans. But when serializing back I think Jackson don't knows what type object has. Thus is serializes it to a LinkedHashMap. Is it possible to add the information which type the object has?
Here is my sample program:
TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {};
ObjectMapper m = new ObjectMapper();
JsonFactory jf = new JsonFactory();
Map<String, Object> map = new HashMap<String, Object>();
UserBean bean1 = new UserBean();
bean1.setId("1");
bean1.setName("test");
map.put("user", bean1);
String test = m.writeValueAsString(map);
map = m.readValue(test, typeRef);
for (final Map.Entry<String, Object> entry : map.entrySet())
{
final String key = entry.getKey();
if (key.matches("user"))
{
final UserBean userBean = (UserBean)map.get(key);
}
}
public class UserBean {
String id;
String name;
public UserBean()
{
super();
}
public void setId(String id){
this.id = id;
}
public String getId(){
return this.id;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
Try doing the map like below;
Map<String, UserBean> map = new HashMap<String, UserBean>();
(If Java 7 you can use use the diamond operator without specifying the types like new HashMap<>()).
You could also try using #JsonDeserialize using jackson-annotations
Related
I have a dynamic incoming JSON like shown below :
1st possibility
{
"key1":"value1",
"key2":"value2",
"key3":{
"inKey1":"inValue1",
"inKey2":"inValue2"
}
}
2nd possibility
{ "key1":"value1", "key2":"value2", "key3":[{
"inKey1":"inValue1",
"inKey2":"inValue2"
},{
"inKey1":"inValue3",
"inKey2":"inValue4"
}] }
The value of key3 is generally a map. But sometimes it can come as an array as well. I have to bind this JSON to a Bean and then proceed further. I am planning to write two beans, one with key3 as a map and the other with key3 as an array. I will check if value of key3 is an instance of map or an array and then bind to the corresponding bean. Is there any optimal way to get this task done with a single bean ? Please guide me.
Beans (which I havent written yet) would be something like :
public class Bean1{
private String key1;
private String key2;
private Map<String, String> key3 = new HashMap<String, String>();
}
public class Bean2{
private String key1;
private String key2;
private Map<String, String> key3[];
}
Deserialization of different json inputs with common properties can be done through generalization (Inheritance).
Define a parent bean : Define a parent bean with common properties
public class ParentBean {
protected String key1;
protected String key2;
public ParentBean(String key1, String key2) {
super();
this.key1 = key1;
this.key2 = key2;
}
// Setters and Getters
}
Define child beans : Define child beans with special properties
Bean1
public class Bean1 extends ParentBean {
private Map<String, String> key3;
public Bean1(String key1, String key2, Map<String, String> key3) {
super(key1, key2);
this.key3 = key3;
}
// Setters and Getters
}
Bean2
public class Bean2 extends ParentBean {
private Map<String, String> key3[];
public Bean2(String key1, String key2, Map<String, String>[] key3) {
super(key1, key2);
this.key3 = key3;
}
// Setters and Getters
}
Design a Deserializer : Since the framework is not specified, I have taken the liberty of using jackson framwork. Using jackson a Deserializer can be designed as follows:
public class ParentBeanDeserializer extends StdDeserializer<ParentBean>{
public ParentBeanDeserializer() {
this(null);
}
public ParentBeanDeserializer(Class<?> c) {
super(c);
}
#Override
public ParentBean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException{
ParentBean pb;
JsonNode node = jp.getCodec().readTree(jp);
String value1 = node.findValue("key1").asText();
String value2 = node.findValue("key2").asText();
JsonNode node3 = node.findValue("key3");
ObjectMapper mapper = new ObjectMapper();
if(node3.isArray()){
String json3 = node3.toString();
Map<String, String>[] map = mapper.readValue(json3, Map[].class);
pb = new Bean2(value1,value2,map);
}
else{
String json3 = node3.toString();
Map<String, String> map = mapper.readValue(json3, Map.class);
pb = new Bean1(value1,value2,map);
}
return pb;
}
}
Usage : Beans/Classes mentioned above can be used as follows:
String json = getJasonResponseAsText();
ObjectMapper om = new ObjectMapper();
SimpleModule mod = new SimpleModule();
mod.addDeserializer(ParentBean.class, new ParentBeanDeserializer());
om.registerModule(mod);
ParentBean pb = om.readValue(json, ParentBean.class);
if (pb instanceof Bean1) {
Bean1 b1 = (Bean1)pb;
//Perform Bean1 related activites
}
else if (pb instanceof Bean2) {
Bean2 b2 = (Bean2)pb;
//Perform Bean2 related activites
}
I'm using org.json the main thing is to check whether the key is instanceof object or array. when you get to know the kind of the key. you can put your logic easily.
In the first case you have object then i simply typecast into object and fetch the values.
in the second case you have the array so i typecast into JSONArary and used a simple for loop to gather the elements.
Code:
package jsontest;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
/**
*
* #author Sahil
*/
class Model1{
String key1;
String key2;
List<Model2> key3;
Model1(){
key3 = new ArrayList<>();
}
#Override
public String toString() {
String s = "key1=" + key1 + ", key2=" + key2;
for(Model2 m: key3){
s+="\n"+"inKey1="+m.key1+", inkey2="+m.key2;
}
return s;
}
}
class Model2{
String key1;
String key2;
}
public class JSONTest {
static Model1 parse(String s){
JSONObject object = new JSONObject(s);
Model1 model = new Model1();
model.key1 = object.getString("key1");
model.key2 = object.getString("key2");
if (object.get("key3") instanceof JSONObject){
JSONObject key3 = object.getJSONObject("key3");
Model2 model2 = new Model2();
model2.key1 = key3.getString("inKey1");
model2.key2 = key3.getString("inKey2");
model.key3.add(model2);
}else{
JSONArray array = object.getJSONArray("key3");
for(int i=0;i<array.length();i++){
JSONObject row = array.getJSONObject(i);
Model2 model2 = new Model2();
model2.key1 = row.getString("inKey1");
model2.key2 = row.getString("inKey2");
model.key3.add(model2);
}
}
return model;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String firstCase = "{\r\n\"key1\":\"value1\",\r\n\"key2\":\"value2\",\r\n\"key3\":{\r\n \"inKey1\":\"inValue1\",\r\n \"inKey2\":\"inValue2\"\r\n }\r\n}";
String secondCase = "{ \"key1\":\"value1\", \"key2\":\"value2\", \"key3\":[{\r\n \"inKey1\":\"inValue1\",\r\n \"inKey2\":\"inValue2\"\r\n },{\r\n \"inKey1\":\"inValue3\",\r\n \"inKey2\":\"inValue4\"\r\n }] }";
System.out.println(parse(firstCase));
System.out.println("---------");
System.out.println(parse(secondCase));
}
}
Result:
key1=value1, key2=value2
inKey1=inValue1, inkey2=inValue2
---------
key1=value1, key2=value2
inKey1=inValue1, inkey2=inValue2
inKey1=inValue3, inkey2=inValue4
I'm using Jackson in a java Rest Api to handle request params.
My Bean class :
public class ZoneModifBeanParam extends ModifBeanParam<Zone> {
#FormParam("type")
private String type;
#FormParam("geometry")
private Geometry geometry;
#FormParam("name")
private String name;
...
My API interface :
#POST
#Consumes("application/json")
#Produces("application/json; subtype=geojson")
#ApiOperation(value = "Create a zone", notes = "To create a zone")
public Response createZone(ZoneModifBeanParam zoneParam) {
...
This Works fine but I need to receive other params that aren't specified by my Bean in a Map.
Example :
{
"geometry": {...},
"name": "A circle name",
"type": "4",
"hello": true
}
By receiving this I need to store in a Map (named unrecognizedFields and declared in my bean) the couple ("hello", true).
Is there any annotation or object allowing this?
Just use #JsonAnySetter. That's what it's made for. Here is a test case
public class JacksonTest {
public static class Bean {
private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
private Map<String, Object> unrecognizedFields = new HashMap<>();
#JsonAnyGetter
public Map<String, Object> getUnrecognizedFields() {
return this.unrecognizedFields;
}
#JsonAnySetter
public void setUnrecognizedFields(String key, Object value) {
this.unrecognizedFields.put(key, value);
}
}
private final String json
= "{\"name\":\"paul\",\"age\":600,\"nickname\":\"peeskillet\"}";
private final ObjectMapper mapper = new ObjectMapper();
#Test
public void testDeserialization() throws Exception {
final Bean bean = mapper.readValue(json, Bean.class);
final Map<String, Object> unrecognizedFields = bean.getUnrecognizedFields();
assertEquals("paul", bean.getName());
assertEquals(600, unrecognizedFields.get("age"));
assertEquals("peeskillet", unrecognizedFields.get("nickname"));
}
}
The #JsonAnyGetter is used on the serialization side. When you serialize the bean, you will not see the unrecognizedFields in the JSON. Instead all the properties in the map will be serialized as top level properties in the JSON.
You may be able to ignore the unrecognized fields safely by configuring the ObjectMapper, however to specifically put them as key-value pairs of a Map field, you'll need your own de-serializer.
Here's a (heavily simplified) example:
Given your POJO...
#JsonDeserialize(using=MyDeserializer.class)
class Foo {
// no encapsulation for simplicity
public String name;
public int value;
public Map<Object, Object> unrecognized;
}
... and your custom de-serializer...
class MyDeserializer extends JsonDeserializer<Foo> {
#Override
public Foo deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// new return object
Foo foo = new Foo();
// setting unrecognized container
Map<Object, Object> unrecognized = new HashMap<>();
foo.unrecognized = unrecognized;
// initializing parsing from root node
JsonNode node = p.getCodec().readTree(p);
// iterating node fields
Iterator<Entry<String, JsonNode>> it = node.fields();
while (it.hasNext()) {
Entry<String, JsonNode> child = it.next();
// assigning known fields
switch (child.getKey()) {
case "name": {
foo.name = child.getValue().asText();
break;
}
case "value": {
foo.value = child.getValue().asInt();
break;
}
// assigning unknown fields to map
default: {
foo.unrecognized.put(child.getKey(), child.getValue());
}
}
}
return foo;
}
}
Then, somewhere...
ObjectMapper om = new ObjectMapper();
Foo foo = om.readValue("{\"name\":\"foo\",\"value\":42,\"blah\":true}", Foo.class);
System.out.println(foo.unrecognized);
Output
{blah=true}
I am trying to unmarshall a json file in a way such that few properties of Json are mapped into a HashMap that is present in my model class.Rest of the properties are mapped to the respective fields of the class.Please find the Json below:
{
"_id":2,
"Name":"xyz",
"Age":20,
"MEMO_TEXT":"yyy",
"MEMO_LINK":"zzz",
"MEMO_DOB":"",
"MEMO_USERNAME":"linie orange",
"MEMO_CATEGORY":2,
"MEMO_UID":"B82071415B07495F9DD02C152E4805EC"
}
And here is the Model class to which I want to map this Json:
public class Model{
private int _id;
private String name;
private int age
private HashMap<String, String> columns;
//Getters and Setter methods
}
So here, what i want is to get a map columns that contains keys "MEMO_TEXT","MEMO_LINK","MEMO_DOB","MEMO_USERNAME","MEMO_CATEGORY","MEMO_UID"
and rest of the properties in Json are mapped to their respective fields.
Is it possible to do this using ObjectMapper of Jackson Library?
You can use #JsonAnySetter to annotate a method to be called for "other" properties:
#Test
public void partial_binding() throws Exception {
Model model = mapper.readValue(Resources.getResource("partial_binding.json"), Model.class);
assertThat(model.name, equalTo("xyz"));
assertThat(model.columns, hasEntry("MEMO_TEXT", "yyy"));
assertThat(
mapper.writeValueAsString(model),
json(jsonObject()
.withProperty("Name", "xyz")
.withProperty("MEMO_TEXT", "yyy")
.withAnyOtherProperties()));
}
public static class Model {
#JsonProperty
private int _id;
#JsonProperty("Name")
private String name;
#JsonProperty("Age")
private int age;
private HashMap<String, String> columns;
#JsonAnyGetter
public HashMap<String, String> getColumns() {
return columns;
}
public void setColumns(HashMap<String, String> columns) {
this.columns = columns;
}
#JsonAnySetter
public void putColumn(String key, String value) {
if (columns == null) columns = new HashMap<>();
columns.put(key, value);
}
}
Also, #JsonAnyGetter does "kind of the reverse", so this should serialize and deserialize the same way.
One of several ways to achieve what you want is to add a constructor:
#JsonCreator
public Model(Map<String, Object> fields) {
this._id = (int) fields.remove("_id");
this.name = (String) fields.remove("Name");
this.age = (int) fields.remove("Age");
this.columns = new HashMap<String, String>();
for (Entry<String, Object> column : fields.entrySet()) {
columns.put(column.getKey(), column.getValue().toString());
}
}
Be aware that if you serialize it back to JSON the structure will be diffrent than the initial one.
Try using a SerializerProvider. A SerializerProvider can modify the deserialization, enabling custom deserialization.
I just started using Jackson JSON parser, and I love it, but I've run into a problem with a JSON object I'm trying to parse.
here's my current java code:
public class resetPassword {
private String id;
private String key1;
private String key2;
public String getId() {
return id;
}
public void setId(String id) {
this.id= id;
}
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;
}
}
how would I parse something like this in Jackson:
{
"1":{
"key1":"val",
"key2":"val"
},
"2":{
"key":"val",
"key":"val"
}, .. etc
}
any help with this would be greatly apreceated
Based on the information in comments, I guess you need to combine traversing with data binding.
First, using traversal, get JsonNode objects with {"key1": ..., "key2": ...}.
Pseudocode (not tested):
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(genreJson);
Iterator<String> fieldNames = root.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode node = root.get(fieldName);
// now you should have {"key1": ...} in node
}
Then use data binding for each node you found:
ResetPassword item = mapper.readValue(node, ResetPassword.class);
If you need a quick way, you can set it to a Map;
ObjectMapper mapper = new ObjectMapper();
Map<String, Map<String, String>> map = mapper.readValue(br, Map.class);
System.out.println(map);
Your map would now be:
{1={key1=val, key2=val}, 2={key1=val, key2=val}}
You can iterate over the Map(s) and set your ResetPassword accordingly.
PS: br is my BufferedReader instance which reads the json placed in numeric.txt,
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("numeric.txt"), "UTF-8"));
I have the following JSON:
"propertyName": "{"1":[{"1":"value1","2":"value2","3":false}]}"
the first property being the count of items in the array following having a map of properties.
What is the best way to deserialize this using Jackson
if I want to fill up a class holding these values:
class MyHolder
{
name = "value1";
age = "value2";
female = false;
}
for instance.
To deserialize to list/collection of concrete values (rather then the LinkedHashMap you get by default) you will need to pass type information to Jackson:
mapper.readValue(jsonString, new TypeReference<List<MyHolder>>() { });
The other way to do the same is:
CollectionType javaType = mapper.getTypeFactory().constructCollectionType(List.class, MyHolder.class);
List<MyDto> asList = mapper.readValue(jsonString, javaType);
Hope this helps.
Your JSON is not valid. Let assume that JSON looks like this:
{
"propertyName":{
"1":[
{
"1":"value1",
"2":"value2",
"3":false
}
]
}
}
The simplest way is to create POJO classes which fit to your JSON. For example:
class Root {
private Map<String, List<MyHolder>> propertyName;
//getters,setters,toString
}
class MyHolder {
#JsonProperty("1")
private String name;
#JsonProperty("2")
private String age;
#JsonProperty("3")
private boolean female;
//getters,setters,toString
}
Now we can easily deserialize it in this way:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue("{.. json ...}", Root.class));
Above program prints:
Root [propertyName={1=[MyHolder [name=value1, age=value2, female=false]]}]
If we do not want to see Map in our POJO class we have to write custom converter:
class MapMyHolderConverter implements Converter<Map<String, List<Map<String, Object>>>, List<MyHolder>> {
#Override
public JavaType getInputType(TypeFactory typeFactory) {
return typeFactory.constructMapType(Map.class, String.class, List.class);
}
#Override
public JavaType getOutputType(TypeFactory typeFactory) {
return typeFactory.constructCollectionType(List.class, MyHolder.class);
}
#Override
public List<MyHolder> convert(Map<String, List<Map<String, Object>>> map) {
Collection<List<Map<String, Object>>> values = map.values();
if (values.isEmpty()) {
return Collections.emptyList();
}
List<MyHolder> result = new ArrayList<>();
for (Map<String, Object> item : values.iterator().next()) {
MyHolder holder = new MyHolder();
holder.setName(item.get("1").toString());
holder.setAge(item.get("2").toString());
holder.setFemale((Boolean) item.get("3"));
result.add(holder);
}
return result;
}
}
Your POJO classes could look like this now:
class Root {
#JsonDeserialize(converter = MapMyHolderConverter.class)
private List<MyHolder> propertyName;
//getters,setters,toString
}
class MyHolder {
private String name;
private String age;
private boolean female;
//getters,setters,toString
}
As you can see in second example we are using #JsonDeserialize annotation and we have not to use #JsonProperty.