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.
Related
I am not very good with Java, so please forgive me for my ignorance.
I am selecting from a table and looping through the result set to make a list that i can use to convert into json which should look like the following example.
{
"ContactId": "123",
"key": {
"type": "email",
"email": "emailAdress"
},
"address": "String",
"source": "String",
"revoked": true,
"text": "String"
}
I don't know how to put them into a list as there are different datatypes i need to put into hash maps.
Please note, in the exmaple above key is an object and i am trying to achieve the same thing in the list.
The ultimate goal is to covert the generated list into json.
I have created a few hashmaps but i don't think i am doing it the right way.
String Table = "TableName";
String sql = String.format("SELECT id_user, email FROM %s ", Table);
ResultSet res = dbConn.prepareStatement(sql).executeQuery();
Map<String, Map> keyObject = new HashMap<String, Map>();
Map<String, String> keyMap = new HashMap<String, String>();
Map<String, String> mainMap = new HashMap<String, String>();
List<Map<String, String>> contacts = new ArrayList<>();
int i = 0;
while(res.next()){
keyMap.put("type", "email");
keyMap.put("value", res.getString("email"));
keyObject.put("key", keyMap);
mainMap.put("tenant", res.getString("id_user"));
mainMap.put("purpose", "Marketing sendouts");
mainMap.put("legalBase", "Withdrawn");
contacts.add(i, map);
i++;
}
System.out.println(contacts);
Instead of using generic structures like List or Map, you'd be better off designing classes which model the structure you need. For instance, in the case above you'll probably have something like:
public class Contact {
private int contactId;
private ContactKey key;
private String address;
private String source;
private boolean revoked;
private String text;
// ....
}
Now this is a strongly typed structure which can be easily filled from the resultset. A tool like Jackson (or many others) can easily render it as JSON.
Create a Model Class and use it's getter and setter methods.
Eg.
public class Model {
private String id;
private String name;
//Use constructor
public Model(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// Now make your Map data Structure center code hereustom .
Hashmap<String,Model> Map = new Hashmap();
// Create a object of your custom Model datastructure and add data.
Model model = new Model("145","Jason");
Map.put("1st",model);
Note that all the data is store at "1st" key of Map.
Kudos.... Enjoy
If map is your preferred data structure, you can use the code below:
Map<String, Object> map = new HashMap<>();
map.put("ContactId", 123);
Map<String, Object> keyMap = new HashMap<>();
keyMap.put("type", "email");
keyMap.put("email", "emailAdress");
map.put("key", keyMap);
map.put("address", "aaaa");
ObjectMapper objectMapper = new ObjectMapper();
try {
String json = objectMapper.writer().withDefaultPrettyPrinter().writeValueAsString(map);
System.out.println(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
I am assuming Jackson is the library you use for serialization.
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}
There's a library class, the definition is:
class LibraryBean {
public String tag;
public Map<String, Object> attributes;
}
In my application code, I know every possible key for the attributes map, and the type of value of a certain key is fixed, for example, if the key is "location" then the value type is Location, if the key is "timestamp" then the value type is Long. Writing an ArrayList<LibraryBean> to a json string is simple, but how can I deserialize the json string so that the value recovers its type?
public void test(ArrayList<LibraryBean> sampleList) {
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeAsString(sampleList);
List<LibraryBean> recovered = mapper.readValue(jsonString); // what should I do here?
// I'm sure sampleList.get(0).attributes.get("location") != null
AssertTrue(sampleList.get(0).attributes.get("location") instanceof Location);
}
Create your pojo for known fields:
class Attributes {
private Location location;
...
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
... getters and setters
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
class LibraryBean {
private String tag;
private Attributes attributes;
... getters and setters
}
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
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.