Jackson deserialization with unknown dynamic properties - java

I have a JSON string like:
"shipping_profiles": {
"563": {
"name": "name",
"value": "value"
},
"564": {
"name": "name",
"value": "value"
},
"565": {
"name": "name",
"value": "value"
},
"566": {
"name": "name",
"value": "value"
}
}
Now I am parsing it with Jackson 2.0.
I am trying to get a List<shipping_profiles> from the JSON string.
Is it possible?

Your shipping_profiles property doesn't look like array. It represent object with dynamic properties, so we should treat it like an object. If we do not know anything about properties we can use #JsonAnySetter annotation. Algorithm could looks like below:
Deserialize JSON into JSON-model classes.
Convert dynamic objects (maps) into app's POJO classes using ObjectMapper
Use app's POJO whenever you want.
Please see my example implementation. I hope, it help you solve your problem. Input JSON:
{
"shipping_profiles":{
"563":{
"name":"name563",
"value":"value563"
},
"564":{
"name":"name564",
"value":"value564"
},
"565":{
"name":"name565",
"value":"value565"
},
"566":{
"name":"name566",
"value":"value566"
}
}
}
Example program:
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
File source = new File("X:/test.json");
Entity entity = mapper.readValue(source, Entity.class);
ShippingProfiles shippingProfiles = entity.getShippingProfiles();
List<Map<String, String>> profileMaps = shippingProfiles.getProfiles();
List<Profile> profiles = new ArrayList<Profile>(profileMaps.size());
for (Map<String, String> item : profileMaps) {
profiles.add(mapper.convertValue(item, Profile.class));
}
System.out.println(profiles);
}
}
class Entity {
#JsonProperty("shipping_profiles")
private ShippingProfiles shippingProfiles;
public ShippingProfiles getShippingProfiles() {
return shippingProfiles;
}
public void setShippingProfiles(ShippingProfiles shippingProfiles) {
this.shippingProfiles = shippingProfiles;
}
}
class ShippingProfiles {
private List<Map<String, String>> profiles = new ArrayList<Map<String, String>>();
#JsonAnySetter
public void setDynamicProperty(String name, Map<String, String> map) {
profiles.add(map);
}
public List<Map<String, String>> getProfiles() {
return profiles;
}
public void setProfiles(List<Map<String, String>> profiles) {
this.profiles = profiles;
}
}
class Profile {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return "Profile [name=" + name + ", value=" + value + "]";
}
}
Above app prints:
[Profile [name=name563, value=value563], Profile [name=name564, value=value564], Profile [name=name565, value=value565], Profile [name=name566, value=value566]]

I got my json with dynamic property parsed with the way #michalziober provide.
"commandClasses": {
"32": {
"name": "Basic",
"data": {
"name": "devices.1.instances.1.commandClasses.32.data",
"value": null,
"type": "NoneType"
},
"38": {
"name": "SwitchMultilevel",
"data": {
"name": "devices.1.instances.1.commandClasses.38.data",
"value": null,
"type": "NoneType"
},
"43": {
"name": "SceneActivation",
"data": {
"name": "devices.1.instances.1.commandClasses.43.data",
"value": null,
"type": "NoneType"
}
With this json I also need to save that dynamic property, so I add another List for storing it.
public class CommandClasses {
private List<String> nameList = new ArrayList<String>();
private List<CommandClass> commmandClasses = new ArrayList<CommandClass>();
private Logger logger = Logger.getInstance(CommandClasses.class);
#JsonAnySetter
public void setDynamicCommandClass(String name, CommandClass cc) {
logger.d("# adding new CC : " + name);
nameList.add(name);
commmandClasses.add(cc);
}
public List<CommandClass> getCommmandClasses() {
return commmandClasses;
}
public void setCommmandClasses(List<CommandClass> commmandClasses) {
this.commmandClasses = commmandClasses;
}
}
Now I can also access the field as id to send out request later.

Related

Put method using a mapped class (use setters for specified parameter) - Spring boot API

I'm trying to make a PUT request for an object using only one function for all parameters. Let's say I have this object structure (JSON):
{
"id": 3,
"name": "test",
"dominio": "dom",
"altas": "6",
"bajas": "2",
"default_group": [
{
"idRef": 1,
"name": "Users",
"path": "OU=es"
}
],
"office": [
{
"idRef": 1,
"title": "Intern",
"name": "CN=Office license",
"path": "OU=licenseOffice"
},
{
"idRef": 2,
"title": "Specialist",
"name": "CN=Office License F3",
"path": "OU=LicenseGroupF"
}
]
}
I managed to do this for a GET Request using a Map function with the getters of the class.
To do this, I passed the attribute name in the HTTP request using a GET Request:
Map<String, Function<Compania, Object>> mapCompania = Map.of(
"name", Compania::getName,
"dominio", Compania::getDominio,
"altas", Compania::getAltas,
"bajas", Compania::getBajas,
"default_group", Compania::getDefault_group,
"office", Compania::getOffice
);
Function<Compania, Object> retriever = mapCompania.get(fieldName);
But now, I can't find a way to implement this same thing but in order to use the setter methods. Something like:
PUT localhost/myClass/3/name --> it uses MyClass.setName(input...)
Or:
PUT localhost/myClass/3/office --> it uses MyClass.setOffice(Object office)
Could anyone help me to achieve this? Thank you very much
Assuming that Compania is as follows:
public class Compania {
private Object name;
private Object dominio;
private Object altas;
private Object bajas;
private Object default_group;
private Object office;
public Object getName() {
return name;
}
public void setName(Object name) {
this.name = name;
}
public Object getDominio() {
return dominio;
}
public void setDominio(Object dominio) {
this.dominio = dominio;
}
public Object getAltas() {
return altas;
}
public void setAltas(Object altas) {
this.altas = altas;
}
public Object getBajas() {
return bajas;
}
public void setBajas(Object bajas) {
this.bajas = bajas;
}
public Object getDefault_group() {
return default_group;
}
public void setDefault_group(Object default_group) {
this.default_group = default_group;
}
public Object getOffice() {
return office;
}
public void setOffice(Object office) {
this.office = office;
}
}
The code below should do the trick:
Map<String, BiConsumer<Compania, Object>> mapCompaniaSetters = Map.of(
"name", Compania::setName,
"dominio", Compania::setDominio,
"altas", Compania::setAltas,
"bajas", Compania::setBajas,
"default_group", Compania::setDefault_group,
"office", Compania::setOffice
);
BiConsumer<Compania, Object> setter = mapCompaniaSetters.get(fieldName);
We can test this as follows to check that it actually works:
public static void main(String[] args) {
Map<String, BiConsumer<Compania, Object>> mapCompaniaSetters = Map.of(
"name", Compania::setName,
"dominio", Compania::setDominio,
"altas", Compania::setAltas,
"bajas", Compania::setBajas,
"default_group", Compania::setDefault_group,
"office", Compania::setOffice
);
BiConsumer<Compania, Object> setter = mapCompaniaSetters.get("name");
Compania compania = new Compania();
System.out.println("Empty Compania: " + compania);
setter.accept(compania, "Test");
System.out.println("Compania with Name: " + compania);
}

Jackson deserialize JSON into pojo with map property

Can somebody help me, how I can deserialize the following JSON, which I can not change?
I am using Jackson for serialization.
{
"columns": [
{
"header": "Heading1",
},
{
"header": "Heading2",
}
],
"rows": [
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
]
}
Columns can have unknown number of headers and their value eg. "Header1" is used in the rows array.
So far I have the following structure:
public class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
public class ColumnConfig {
private String header;
}
public class QueryResultRow {
private int id;
private Map<String, CellValue> values;
}
public class CellValue{
private String value;
}
The problem is that the Map is empty when I deserialize into QueryResult;
I read about TypeReference but I do not know how I can specify a TypeReference<HashMap<String,CellValue>> for the property values in QueryResultRow.
Edit:
My ObjectMapper code is the following:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String queryResultString = loadQuery(queryPath);
QueryResult result = mapper.readValue(queryResultString, QueryResult.class);
The content of queryResultString is the JSON above.
First problem is your JSON is invalid. I assume it should be something like this,
{
"columns": [
{
"header": "Heading1"
},
{
"header": "Heading2"
}
],
"rows": [
{
"id": 1,
"Heading1": {
"value": "Value1"
},
"Heading2": {
"value": "Value2"
}
}
]
}
Then answer is quite straightforward. You need to change your QueryResultRow as follows,
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
Then I think you should good to go.
Here is a complete working example,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"columns\":[{\"header\":\"Heading1\"},{\"header\":\"Heading2\"}],\"rows\":[{\"id\":1,\"Heading1\":{\"value\":\"Value1\"},\"Heading2\":{\"value\":\"Value2\"}}]}";
ObjectMapper om = new ObjectMapper();
QueryResult queryResult = om.readValue(s, QueryResult.class);
System.out.println(queryResult);
}
}
#Getter
#Setter
#ToString
class QueryResult {
private ColumnConfig[] columns;
private QueryResultRow[] rows;
}
#Getter
#Setter
#ToString
class ColumnConfig {
private String header;
}
#Getter
#Setter
#ToString
class QueryResultRow {
private int id;
private Map<String, CellValue> values = new HashMap<>();
#JsonAnySetter
public void addValues(String k, CellValue v) {
values.put(k, v);
}
}
#Getter
#Setter
#ToString
class CellValue{
private String value;
}

How to parse nested JSON object in java

Here is the sample JSON string. I want to parse this nested JSON object even though nested object have the same name. Some time we may have multiple levels of the nested objects. I tired with Jackson nested objects parsing but that did not work for me. After parsing the object, i want to convert that into a different format.Please help me in parsing this JSON. Thanks in advance.
{
"operator": "and",
"predicates": [
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "equal",
"value": "xxxx"
},
{
"columnName": "Age",
"datatype": "number",
"input": "number",
"operand": "greater_or_equal",
"value": "21"
}
]
},
{
"operator": "and",
"predicates": [
{
"columnName": "userName",
"datatype": "string",
"input": "text",
"operand": "not_equal",
"value": "nnn"
},
{
"columnName": "Birthday",
"datatype": "date",
"input": "date_picker",
"operand": "in",
"value": "2020-07-23,2020-07-24"
}
]
}
]
}
below is the code in java
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Predicates {
private String columnName;
private String datatype;
private String input;
private String operator;
private String value;
private String operand;
/**
*
*/
private List<Predicates> predicates;
#JsonProperty("predicates")
private void unpackNested(Map<String,Object> predicates) {
this.columnName = (String)predicates.get("columnName");
this.datatype = (String)predicates.get("datatype");
this.input = (String)predicates.get("input");
this.operator = (String)predicates.get("operator");
this.value = (String)predicates.get("value");
this.operand = (String)predicates.get("operand");
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getDatatype() {
return datatype;
}
public void setDatatype(String datatype) {
this.datatype = datatype;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getOperand() {
return operand;
}
public void setOperand(String operand) {
this.operand = operand;
}
public List<Predicates> getPredicates() {
return predicates;
}
public void setPredicates(List<Predicates> predicates) {
this.predicates = predicates;
}
}
Parsing
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
I don't know what you are trying to achieve with your unpackNested method. Jackson can already bind properties from your JSON to your objects and handles recursive properties just fine.
I simply removed your unpackNested method and ran your code on your provided input:
ObjectMapper mapper = new ObjectMapper();
Predicates pr = mapper.readValue(json, Predicates.class);
The object pr contains the full json including the nested child predicates. (I ran the code with jackson databind 2.11.2).
If your properties are not auto-detected, annotate your getters with #JsonProperty:
class Predicate {
// ..snip..
#JsonProperty("input")
public String getInput() {
return input;
}
#JsonProperty("predicates")
public List<Predicates> getPredicates() {
return predicates;
}
}
But apart from that, no extra steps need to be taken. Jackson can already unpack nested objects, just remove your (weird) unpackNested method and let Jackson do its job.

JSON Array of objects to plain old java object

this is my first time making an external api call in Java, so please bear with me as I'm not very experienced. I got the http request working and got a response, but now I need to parse it.
I'm trying to convert a json array to java objects. I understand the gist of it, but all examples I've seen don't apply to my issue.
I need the 'entities' objects from the json string. The details (which are an array, too) can contain any key/value pair, so I was thinking of putting that in a hashmap in each Entity object. I've tried the gson library, but I can't find any gson example that goes deeper than a single dimensional json array.
I realize this is kind of a broad question, and I don't expect anyone to deliver me a working solution, but a few tips or a link to a relevant guide would go a long way. :)
{
"return": {
"entities": [
{
"id": 2385,
"details": [
{
"name": "Other Known Name",
"value": "John Wick",
"match": false
}
],
"proofs": [],
"link": "http://domain.gg/users?id=2385"
},
{
"id": 2384,
"details": [
{
"name": "Discord ID",
"value": "159985870458322944",
"match": false
},
{
"name": "SteamID64",
"value": "76561197991558078",
"match": true
},
{
"name": "SteamVanity",
"value": "test",
"match": false
},
{
"name": "PS4",
"value": "John_S",
"match": false
},
{
"name": "XBox",
"value": "John S",
"match": false
},
{
"name": "Email",
"value": "john_smith#gmail.com",
"match": true
},
{
"name": "Comment",
"value": "Test user",
"match": false
},
{
"name": "Other Known Name",
"value": "Jonathan",
"match": false
},
{
"name": "Reddit",
"value": "/u/johns",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=2384"
},
{
"id": 1680,
"details": [
{
"name": "Other Known Name",
"value": "Johny",
"match": false
},
{
"name": "SteamID64",
"value": "76561198213003675",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=1680"
},
{
"id": 1689,
"details": [
{
"name": "Other Known Name",
"value": "JohnnyPeto",
"match": false
},
{
"name": "SteamID64",
"value": "76561198094228192",
"match": true
}
],
"proofs": [],
"link": "http://domain.gg/users?id=1689"
}
],
"notice": "Showing 4 out of 4 matches."
}
}
There are many json serialization/deserialization frameworks available. I would recommend having a look at Jackson.
Basically, you have to create Model corresponding to json schema and deserialize json into object. Based on the example in the question, model will look like this:
#JsonIgnoreProperties(ignoreUnknown = true)
class Response {
#JsonProperty("return")
private ResponseObject responseObject;
public ResponseObject getResponseObject() {
return responseObject;
}
public void setResponseObject(ResponseObject responseObject) {
this.responseObject = responseObject;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class ResponseObject {
private List<Entity> entities;
public List<Entity> getEntities() {
return entities;
}
public void setEntities(List<Entity> entities) {
this.entities = entities;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Entity {
private String id;
private List<Details> details;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Details> getDetails() {
return details;
}
public void setDetails(List<Details> details) {
this.details = details;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Details {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Once the model is defined, you can use ObjectMapper class to perform serialization/deserialization, e.g.:
ObjectMapper mapper = new ObjectMapper();
Response response = mapper.readValue("{\"return\": {\"entities\": [{\"id\": 2385,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"John Wick\",\"match\": false}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2385\"},{\"id\": 2384,\"details\": [{\"name\": \"Discord ID\",\"value\": \"159985870458322944\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561197991558078\",\"match\": true},{\"name\": \"SteamVanity\",\"value\": \"test\",\"match\": false},{\"name\": \"PS4\",\"value\": \"John_S\",\"match\": false},{\"name\": \"XBox\",\"value\": \"John S\",\"match\": false},{\"name\": \"Email\",\"value\": \"john_smith#gmail.com\",\"match\": true},{\"name\": \"Comment\",\"value\": \"Test user\",\"match\": false},{\"name\": \"Other Known Name\",\"value\": \"Jonathan\",\"match\": false},{\"name\": \"Reddit\",\"value\": \"/u/johns\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2384\"},{\"id\": 1680,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"Johny\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198213003675\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1680\"},{\"id\": 1689,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"JohnnyPeto\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198094228192\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1689\"}],\"notice\": \"Showing 4 out of 4 matches.\"}}", Response.class);
System.out.println(response.getResponseObject().getEntities().get(0).getId());
Here's the Javadoc.
If I were you, I'd use Jackson, not GSON. It's specialized on JavaBeans-style mapping. Write classes like this:
public class Detail{
private String name;
private String value;
private boolean match;
// + getters / setters
}
public class Entity{
private int id;
private List<Detail> details;
private String link;
private List<String> proofs;
// you don't have any example data for this, so I'm assuming strings
// + getters / setters
}
public class Result{
private List<Entity> entities;
private String notice;
// + getters / setters
}
and do the conversion with something like
Result result = new ObjectMapper().readValue(json, Result.class);
As my fellow stackoverflow users have previously posted, for this kind of initilization Jackson API would be better. I have however posted the solution for your question with Gson.
I noticed that you like your details to be stored as a HashMap with id as key. However, it seems like this id is actually related to the entities and not to the details.
Disclaimer, I got lazy and used an online POJO generator because I did not want to create objects for all of the Json elements ;) It still showcases how it should be done:
class Main{
public static void main(String[] args) throws FileNotFoundException {
//this is just to load the json file
String input = new Scanner(new File("test.txt")).useDelimiter("\\Z").next();
System.out.println(input);
Gson gson = new Gson();
Example arr = gson.fromJson(input, Example.class);
System.out.println(arr);
}
public class Detail {
#SerializedName("name")
#Expose
public String name;
#SerializedName("value")
#Expose
public String value;
#SerializedName("match")
#Expose
public Boolean match;
#Override
public String toString() {
return "Detail [name=" + name + ", value=" + value + ", match=" + match + "]";
}
}
public class Entity {
#SerializedName("id")
#Expose
public Integer id;
#SerializedName("details")
#Expose
public List<Detail> details = null;
#SerializedName("proofs")
#Expose
public List<Object> proofs = null;
#SerializedName("link")
#Expose
public String link;
#Override
public String toString() {
return "Entity [id=" + id + ", details=" + details + ", proofs=" + proofs + ", link=" + link + "]";
}
}
public class Example {
#SerializedName("return")
#Expose
public Return _return;
#Override
public String toString() {
return "Example [_return=" + _return + "]";
}
}
public class Return {
#SerializedName("entities")
#Expose
public List<Entity> entities = null;
#SerializedName("notice")
#Expose
public String notice;
#Override
public String toString() {
return "Return [entities=" + entities + ", notice=" + notice + "]";
}
}
}
Output
Example [_return=Return [entities=[Entity [id=2385, details=[Detail [name=Other Known Name, value=John Wick, match=false]], proofs=[], link=http://domain.gg/users?id=2385], Entity [id=2384, details=[Detail [name=Discord ID, value=159985870458322944, match=false], Detail [name=SteamID64, value=76561197991558078, match=true], Detail [name=SteamVanity, value=test, match=false], Detail [name=PS4, value=John_S, match=false], Detail [name=XBox, value=John S, match=false], Detail [name=Email, value=john_smith#gmail.com, match=true], Detail [name=Comment, value=Test user, match=false], Detail [name=Other Known Name, value=Jonathan, match=false], Detail [name=Reddit, value=/u/johns, match=true]], proofs=[], link=http://domain.gg/users?id=2384], Entity [id=1680, details=[Detail [name=Other Known Name, value=Johny, match=false], Detail [name=SteamID64, value=76561198213003675, match=true]], proofs=[], link=http://domain.gg/users?id=1680], Entity [id=1689, details=[Detail [name=Other Known Name, value=JohnnyPeto, match=false], Detail [name=SteamID64, value=76561198094228192, match=true]], proofs=[], link=http://domain.gg/users?id=1689]], notice=Showing 4 out of 4 matches.]]
Despite there are answers suggesting you to use Jackson, you can still accomplish easily with Gson with its default configuration just creating proper relations between mappings:
// A generic response, parameterized with <T>, can hold any type except of primitives
final class Response<T> {
#SerializedName("return")
final T ret = null;
}
final class EntitiesAndNotice {
final List<Entity> entities = null;
final String notice = null;
}
final class Entity {
// Unlike Object and any its subclasses, `int` being a primitive cannot be nulled
// Simple 0 won't work either, because the compiler will inline it
// So it's a sort of cheating javac to return a value that holds 0 already
final int id = Integer.valueOf(0);
final List<Detail> details = null;
// Your JSON document does not provide enough info on the elements type
// So it depends on how Gson parses JSON tokens
final List<Object> proofs = null;
final URL link = null;
}
final class Detail {
final String name = null;
final String value = null;
// The same for primitive booleans, or Boolean.FALSE
final boolean match = Boolean.valueOf(false);
}
Example use:
private static final String JSON = "{\"return\":{\"entities\":[{\"id\":2385,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"John Wick\",\"match\":false}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2385\"},{\"id\":2384,\"details\":[{\"name\":\"Discord ID\",\"value\":\"159985870458322944\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561197991558078\",\"match\":true},{\"name\":\"SteamVanity\",\"value\":\"test\",\"match\":false},{\"name\":\"PS4\",\"value\":\"John_S\",\"match\":false},{\"name\":\"XBox\",\"value\":\"John S\",\"match\":false},{\"name\":\"Email\",\"value\":\"john_smith#gmail.com\",\"match\":true},{\"name\":\"Comment\",\"value\":\"Test user\",\"match\":false},{\"name\":\"Other Known Name\",\"value\":\"Jonathan\",\"match\":false},{\"name\":\"Reddit\",\"value\":\"/u/johns\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2384\"},{\"id\":1680,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"Johny\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198213003675\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1680\"},{\"id\":1689,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"JohnnyPeto\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198094228192\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1689\"}],\"notice\":\"Showing 4 out of 4 matches.\"}}";
private static final Gson gson = new Gson();
private static final TypeToken<Response<EntitiesAndNotice>> responseTypeToken = new TypeToken<Response<EntitiesAndNotice>>() {
};
public static void main(final String... args) {
final Response<EntitiesAndNotice> response = gson.fromJson(JSON, responseTypeToken.getType());
final String value = response.ret.entities.get(1).details.get(3).value;
System.out.println(value);
}
Output:
John_S

jackson deserialize list of custom objects

I have the following JSON which I'm trying to deserialize using the Jackson API
"attachments": {
"file1": {
"content": "",
"name": "sample.json",
"type": "application/json"
},
"file2": {
"content": ""
"name": "myspreadsheet.xlsx",
"type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}
},
I basically need an Attachment class which has a list of AttachmentFile objects which would look like this:
public static AttachmentFile {
String content;
String name;
String type;
}
How can I achieve this using a custom deserializer?
Thanks!
I use jackson 1.9.12 and there are no problems serialize and deserialize HashMap.
Attachments:
import java.util.Map;
public class Attachments
{
//#JsonDeserialize(as=HashMap.class) // use this if you want a HashMap
public Map<String, AttachmentFile> attachments;
public Attachments() {
}
public Attachments(
final Map<String, AttachmentFile> attachments
) {
this.attachments = attachments;
}
}
AttachmentFile:
public class AttachmentFile
{
public String content;
public String name;
public String type;
public AttachmentFile() {
}
public AttachmentFile(
final String content,
final String name,
final String type
) {
this.content = content;
this.name = name;
this.type = type;
}
}
Test:
import java.util.HashMap;
import java.util.Map;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.junit.Assert;
import org.junit.Test;
public class AttachmentsTest
{
#Test
public void test()
{
try {
final Map<String, AttachmentFile> attachments = new HashMap<String, AttachmentFile>();
attachments.put(
"file1",
new AttachmentFile(
"",
"sample.json",
"application/json"
)
);
attachments.put(
"file2",
new AttachmentFile(
"",
"myspreadsheet.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
);
final Attachments inputData = new Attachments();
inputData.attachments = attachments;
final ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
final String jsonString = jsonMapper.writeValueAsString(inputData);
//System.out.println(jsonString);
final Attachments outputData = jsonMapper.readValue(jsonString, inputData.getClass());
Assert.assertNotNull(outputData);
Assert.assertEquals(inputData.attachments.size(), outputData.attachments.size());
Assert.assertEquals(inputData.attachments.get("file1").name, outputData.attachments.get("file1").name);
Assert.assertEquals(inputData.attachments.get("file2").name, outputData.attachments.get("file2").name);
} catch (final Exception e) {
Assert.fail(e.getMessage());
}
}
}
You do not need a custom deserializer.
Using jacksons #JsonAnySetter annotation, you can write a method in your attachment class that looks like this
class Attachment
{
ArrayList files = new ArrayList();
#JsonAnySetter
public void setFile(String name, Object value)
{
files.add(value);
}
}
You may have to tweak that code (using more annotations), to make sure that value is deserialized as AttachmentFile. But I think you get the basic idea.

Categories