Consider json:
{
"name": "myName",
"myNestedJson": "{\"key\":\"value\"}"
}
Should be parsed into classes:
public class MyDto {
String name;
Attributes myNestedJson;
}
public class Attributes {
String key;
}
Can it be parsed without writing stream parser? (Note that myNestedJson contains json escaped json string)
I think you can add a constructor to Attributes that takes a String
class Attributes {
String key;
public Attributes() {}
public Attributes(String s) {
// Here, s is {"key":"value"} you can parse it into an Attributes
// (this will use the no-arg constructor)
try {
ObjectMapper objectMapper = new ObjectMapper();
Attributes a = objectMapper.readValue(s, Attributes.class);
this.key = a.key;
} catch(Exception e) {/*handle that*/}
}
// GETTERS/SETTERS
}
Then you can parse it this way:
ObjectMapper objectMapper = new ObjectMapper();
MyDto myDto = objectMapper.readValue(json, MyDto.class);
This is a little dirty but your original JSON is too :)
Related
I have the following JSON structure:
{
"sets": [
{...},
{
"id": "id",
"html": "<html></html>",
"javascript": "{onError: function (event) {}}",
"css": ".someclass {}",
"translations": {
"en": { "LABEL_1": "Some label text", "HEADER_1": "Some header text" },
"fr": { "LABEL_1": "Some label text", "HEADER_1": "Some header text" }
}
}
]
}
I have an object that represents this JSON for deserialization purpose
#Data //Lombok
#NoArgsConstructor //Lombok
public class Set {
#JsonProperty("screenSetID")
private String screenSetID;
#JsonProperty("html")
private String html;
#JsonProperty("css")
private String css;
#JsonProperty("javascript")
private String javascript;
#JsonProperty("translations")
private String translations;
}
I have the following piece of code to deserialize JSON
private List<Set> parseSetsData(String json) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.readValue(json, TypeReference<List<Set>(){});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
How I can parse translations JSON object as it is?
Any Jason object could be mapped to Map<String, Object> and any Json List could be mapped to List<Object>. So you could parse your JSON as follows:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new JavaTimeModule());
objectReader = objectMapper.reader();
Map<String, Object> parsedJson = (Map<String, Object>)objectReader.forType(Map.class).readValue(jsonString)
Note that your JSON is a JSON object and not JSON list, so this example parses it to Map
I'm sure you understand that the that you want is part of on array hats why I had to use a for loop:
ObjectMapper mapper = new ObjectMapper();
JsonNode treeNode = mapper.readTree(json);
treeNode=treeNode.findValue("sets");
for (Iterator<JsonNode> it = treeNode.elements(); it.hasNext(); ) {
JsonNode node = it.next();
String theValueYouWant=node.findValue("translations").textValue();
}
Json I found that when you deserialize your item it should look like the following;
public class En
{
#JsonProperty("LABEL_1")
public String lABEL_1;
#JsonProperty("HEADER_1")
public String hEADER_1;
}
public class Fr
{
#JsonProperty("LABEL_1")
public String lABEL_1;
#JsonProperty("HEADER_1")
public String hEADER_1;
}
public class Translations
{
public En en;
public Fr fr;
}
public class Set
{
public String id;
public String html;
public String javascript;
public String css;
public Translations translations;
}
However, in your class, "translations" is a string, but in a JSON structure, it points to an object.
As input for my Application I might get either a single JsonObject, or a List of them:
input1 = [ { "prop": "val1" }, { "prop": "val2" } ]
input2 = { "prop": "val" }
I can use JsonNode as target type for both inputs
objectMapper.readValue(input1, JsonNode.class);
objectMapper.readValue(input2, JsonNode.class);
And then evaluate whether the root node is a ArrayNode or ObjectNode.
I seek a way to define my custom target type, like a List<MyObject> which has one Element if a JsonObject is provided, or zero to multiple, if a List is provided.
objectMapper.readValue(input, new TypeRef<ArrayList<MyObject>>() {});
however fails for the single object - it can not construc an Array-Type from {.
I was trying to create my own type:
public class MyList extends ArrayList<MyObject> {
public String prop;
#JsonCreator
public MyList(String prop) {
super();
this.prop = prop; // Resp add(new MyObject(prop));
}
public MyList() {}
}
But Jackson refuses to use the JsonCreator for single objects.
Is there any way, I could do that (ideally without a custom serializer, unless that one can be made pretty generic)
Of course, Jackson has an easy solution for that:
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to your help!
#EqualsAndHashCode
public class Example {
#JsonProperty public String name
}
#Test
public void experiment() {
ObjectMapper om = new ObjectMapper();
om.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
String list= "[{ \"name\": \"peter\" }]";
String single= "{ \"name\": \"peter\" }";
List<Example> respList = om.readValue(list, new TypeReference<List<Example>>() {});
List<Example> respSingle = om.readValue(single, new TypeReference<List<Example>>() {});
Assert.assertEquals(respList, respSingle)
}
Consider the following json, getting from an public API:
anyObject : {
attributes: [
{
"name":"anyName",
"value":"anyValue"
},
{
"name":"anyName",
"value":
{
"key":"anyKey",
"label":"anyLabel"
}
}
]
}
As you can see, sometimes the value is a simple string and sometimes its an object. Is it somehow possible to deserialize those kind of json-results, to something like:
class AnyObject {
List<Attribute> attributes;
}
class Attribute {
private String key;
private String label;
}
How would I design my model to cover both cases. Is that possible ?
Despite being hard to manage as others have pointed out, you can do what you want. Add a custom deserializer to handle this situation. I rewrote your beans because I felt your Attribute class was a bit misleading. The AttributeEntry class in the object that is an entry in that "attributes" list. The ValueObject is the class that represents that "key"/"label" object. Those beans are below, but here's the custom deserializer. The idea is to check the type in the JSON, and instantiate the appropriate AttributeEntry based on its "value" type.
public class AttributeDeserializer extends JsonDeserializer<AttributeEntry> {
#Override
public AttributeEntry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode root = p.readValueAsTree();
String name = root.get("name").asText();
if (root.get("value").isObject()) {
// use your object mapper here, this is just an example
ValueObject attribute = new ObjectMapper().readValue(root.get("value").asText(), ValueObject.class);
return new AttributeEntry(name, attribute);
} else if (root.get("value").isTextual()) {
String stringValue = root.get("value").asText();
return new AttributeEntry(name, stringValue);
} else {
return null; // or whatever
}
}
}
Because of this ambiguous type inconvenience, you will have to do some type checking throughout your code base.
You can then add this custom deserializer to your object mapper like so:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(AttributeEntry.class, new AttributeDeserializer());
objectMapper.registerModule(simpleModule);
Here's the AttributeEntry:
public class AttributeEntry {
private String name;
private Object value;
public AttributeEntry(String name, String value) {
this.name = name;
this.value = value;
}
public AttributeEntry(String name, ValueObject attributes) {
this.name = name;
this.value = attributes;
}
/* getters/setters */
}
Here's the ValueObject:
public class ValueObject {
private String key;
private String label;
/* getters/setters */
}
I have a JSON array like as shown below which I need to serialize it to my class. I am using Jackson in my project.
[
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc1",
"clientValue": {}
},
{
"clientId": "111",
"clientName": "mask",
"clientKey": "abc2",
"clientValue": {}
}
]
In above JSON array, clientValue will have another JSON object in it. How can I serialize my above JSON array into my java class using Jackson?
public class DataRequest {
#JsonProperty("clientId")
private String clientId;
#JsonProperty("clientName")
private int clientName;
#JsonProperty("clientKey")
private String clientKey;
#JsonProperty("clientValue")
private Map<String, Object> clientValue;
//getters and setters
}
I have not used jackson before so I am not sure how can I use it to serialize my JSON array into Java objects? I am using jackson annotation here to serialize stuff but not sure what will be my next step?
You can create a utility function shown below. You may want to change the Deserialization feature based on your business needs. In my case, I did not want to fail on unknown properties => (FAIL_ON_UNKNOWN_PROPERTIES, false)
static <T> T mapJson(String body,
com.fasterxml.jackson.core.type.TypeReference<T> reference) {
T model = null;
if(body == null) {
return model;
}
com.fasterxml.jackson.databind.ObjectMapper mapper =
new com.fasterxml.jackson.databind.ObjectMapper();
mapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
try {
model = mapper.readValue(body, reference);
} catch (IOException e) {
//TODO: log error and handle accordingly
}
return model;
}
You can call it using similar approach as shown below:
mapJson(clientValueJsonString,
new com.fasterxml.jackson.core.type.TypeReference<List<DataRequest>>(){});
You can try #JsonAnyGetter and #JsonAnySetter annotations with an inner class object. Also clientName should have type String, not int.
public class DataRequest {
private String clientId;
private String clientName;
private String clientKey;
private ClientValue clientValue;
//getters and setters
}
public class ClientValue {
private Map<String, String> properties;
#JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
#JsonAnyGetter
public Map<String,String> getProperties() {
return properties;
}
}
I have this method:
private String serializeToJson(T item) {
String json;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
json = ow.writeValueAsString(item);
} catch (IOException e) {
e.printStackTrace();
json = "";
}
return json;
}
with this item:
and yet json equals:
{
"saveDate" : "12:29:29 29-Mar-02015"
}
why is failureDict not serialize?
and this is the item:
public class FailedResponses {
HashMap<String, Set<String>> failuresDict;
public String saveDate;
public FailedResponses() {
failuresDict = new HashMap<>();
}
Jackson will work magic on public fields or public getters and setters. I'd recommend that you make the fields of your object private for better encapsulation, and add the public getters/setters to allow jackson to de/serialize it.
Personally I like to use the jackson annotations to make it explicit what the object is being used for, and so that you have full control over the naming of the fields that jackson creates, without having to create non-idiomatic getter/setter or variable names