ObjectMapper readValue - java

I load a ressource file json
with the text format
{
"sources": [{
"prop1": "1",
"prop2": "2"
},
{
"prop1": "1",
"prop2": "2"
},
],
"redirection": [{
"prop1": "1",
"prop2": "2"
}
]
}
I have a class with this properties prop1 and prop2
I want to recover with ObjectMapper a list class. What the method ?
This code doesn't work ....
Map<String, Object> mp = mapper.readValue(jsonResource.getInputStream(),new TypeReference<Map<String, Object>>() {});
String sourceText= new ObjectMapper().readTree(jsonResource.getInputStream()).get("sources").asText();
mapper.readValue(sourceText, new TypeReference<List<MyClass>>(){});
Thanks for your help

In your case, I would write a custom JsonDeserializer. Haven't really tested the code, but I think the idea is clear:
final MyClassDeserializer myClassDeserializer = new MyClassDeserializer();
final SimpleModule deserializerModule = new SimpleModule();
deserializerModule.addDeserializer(MyClass.class, myClassDeserializer);
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(deserializerModule);
And the code for JsonDeserializer:
public class MyClassDeserializer extends JsonDeserializer<MyClass> {
#Override
public MyClass deserialize(final JsonParser jsonParser, final DeserializationContext context)
throws IOException {
final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
final JsonNode sourcesNode = node.get("sources");
if(node.isArray()) {
final ArrayNode arrayNode = (ArrayNode) node;
final Iterable<JsonNode> nodes = arrayNode::elements;
final Set<Source> set = StreamSupport.stream(nodes.spliterator(), false)
.map(mapper)
.collect(Collectors.toSet());
...
}
...
}

First thing: Your JSON is invalid. There is a comma after the second object in the sources array. This has to be deleted.
Second: I think you didn't choose the right type for your result. What your JSON represents is a map which maps from string to an array of objects. So the type should be something like Map<String, Props[]> (Since you didn't provide the name of your class, I called it Props.
With these considerations you can construct a MapType by using ObjectMappers getTypeFactory() method and deserialize the value using the constructed type like shown below.
ObjectMapper mapper = new ObjectMapper();
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Props[].class);
Map<String, Props[]> map = mapper.readValue(s, mapType);

I actually voted for the other answer, but this is my idea, to create the classes and let jackson do the work :
public class ResourceTest {
#Test
public void test1() throws IOException {
assertTrue(true);
Resource resource = new Resource();
resource.getRedirectrions().add(makeRedirectrion("rprop11", "rprop12"));
resource.getRedirectrions().add(makeRedirectrion("rprop21", "rprop22"));
resource.getSources().add(makeSource("sprop11","sprop12"));
resource.getSources().add(makeSource("sprop21","sprop22"));
String json = new ObjectMapper().writeValueAsString(resource);
System.out.println(json);
Resource resource1 = new ObjectMapper().readValue(json, Resource.class);
System.out.println(resource1);
}
private Source makeSource(String prop1, String prop2) {
Source source = new Source();
source.setProp1(prop1);
source.setProp2(prop2);
return source;
}
private Redirectrion makeRedirectrion(String prop1, String prop2) {
Redirectrion redirectrion = new Redirectrion();
redirectrion.setProp1(prop1);
redirectrion.setProp2(prop2);
return redirectrion;
}
}
Output is:
{"sources":[{"prop1":"sprop11","prop2":"sprop12"},{"prop1":"sprop21","prop2":"sprop22"}],"redirectrions":[{"prop1":"rprop11","prop2":"rprop12"},{"prop1":"rprop21","prop2":"rprop22"}]}
Resource{sources=[Source{prop1='sprop11', prop2='sprop12'}, Source{prop1='sprop21', prop2='sprop22'}], redirectrions=[Source{prop1='rprop11', prop2='rprop12'}, Source{prop1='rprop21', prop2='rprop22'}]}

Related

Jackson deserialise JSON with many dynamic nodes

I'm trying to deserialize a JSON file with the format of
{
"English": {
"hex": "FF0000"
},
"Spanish": {
"hex": "0000FF"
},
"Japanese": {
"hex": "FFFF00"
}
}
But I don't want to create a class for each language (thousands) so I wrote a custom LanguageDeserializer which gives me back the List<Language> that I want
static class LanguageDeserializer extends StdDeserializer<ArrayList<Language>> {
//...
#Override
public ArrayList<Language> deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final JsonNode node = jp.getCodec().readTree(jp);
final Iterator<Map.Entry<String, JsonNode>> nodes = node.fields();
final ArrayList<Language> results = new ArrayList<>();
while (nodes.hasNext()) {
// Builds up the object from the nodes
results.add(builder.build());
}
return results;
}
I have a parent class to wrap the results:
public class LanguageWrapper {
#JsonDeserialize(using = Language.LanguageDeserializer.class)
public List<Language> languages = new ArrayList<>();
}
So when I try and use it
final LanguageWrapper languageWrapper = objectMapper.readValue(new ClassPathResource("data/languages.json").getFile(), LanguageWrapper.class);
The languages list is always empty.
Can I do this without needing the LanguageDeserializer or how do I make it work?
Yeah was clearly overthinking it. As #chrylis suggested a Map was the right direction to go in.
Simple as:
final TypeReference<HashMap<String, Language>> typeRef = new TypeReference<>() {};
final HashMap<String, Language> hashMap = objectMapper.readValue(new ClassPathResource("data/languages.json").getFile(), typeRef);
If list (and plain Language class) suits you better, you can do:
TypeReference<Map<String, Map<String, String>>> typeReference = new TypeReference<>() {};
List<Language> languages = new ObjectMapper().readValue(new ClassPathResource("data/languages.json").getFile(), typeReference)
.entrySet()
.stream()
.map(entry -> new Language(entry.getKey(), entry.getValue().get("hex")))
.collect(Collectors.toList());

Deserialize Lists and Objects to the same Structure with Jackson

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)
}

How to convert a json string to java map [duplicate]

I have the following class:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import java.io.Serializable;
import java.util.HashMap;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Theme implements Serializable {
#JsonProperty
private String themeName;
#JsonProperty
private boolean customized;
#JsonProperty
private HashMap<String, String> descriptor;
//...getters and setters for the above properties
}
When I execute the following code:
HashMap<String, Theme> test = new HashMap<String, Theme>();
Theme t1 = new Theme();
t1.setCustomized(false);
t1.setThemeName("theme1");
test.put("theme1", t1);
Theme t2 = new Theme();
t2.setCustomized(true);
t2.setThemeName("theme2");
t2.setDescriptor(new HashMap<String, String>());
t2.getDescriptor().put("foo", "one");
t2.getDescriptor().put("bar", "two");
test.put("theme2", t2);
String json = "";
ObjectMapper mapper = objectMapperFactory.createObjectMapper();
try {
json = mapper.writeValueAsString(test);
} catch (IOException e) {
e.printStackTrace();
}
The json string produced looks like this:
{
"theme2": {
"themeName": "theme2",
"customized": true,
"descriptor": {
"foo": "one",
"bar": "two"
}
},
"theme1": {
"themeName": "theme1",
"customized": false,
"descriptor": null
}
}
My problem is getting the above json string to de-serizlize back into a
HashMap<String, Theme>
object.
My de-serialization code looks like this:
HashMap<String, Themes> themes =
objectMapperFactory.createObjectMapper().readValue(json, HashMap.class);
Which de-serializes into a HashMap with the correct keys, but does not create Theme objects for the values. I don't know what to specify instead of "HashMap.class" in the readValue() method.
Any help would be appreciated.
You should create specific Map type and provide it into deserialization process:
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Theme.class);
HashMap<String, Theme> map = mapper.readValue(json, mapType);
You can use TypeReference class which does the type casting for map with user defined types. More documentation at https://github.com/FasterXML/jackson-databind/
ObjectMapper mapper = new ObjectMapper();
Map<String,Theme> result =
mapper.readValue(src, new TypeReference<Map<String,Theme>>() {});
You can make a POJO that extends a Map.
This is important for dealing with nested maps of objects.
{
key1: { nestedKey1: { value: 'You did it!' } }
}
This can be deserialized via:
class Parent extends HashMap<String, Child> {}
class Child extends HashMap<String, MyCoolPojo> {}
class MyCoolPojo { public String value; }
Parent parent = new ObjectMapper().readValue(json, Parent.class);
parent.get("key1").get("nestedKey1").value; // "You did it!"

jackson serialization for Java object with Map?

I have a Java class like this and want to convert to JSON using Jackson. Thanks for your help.
Java Class
public class myClass {
String Id;
Map<String, Object> optionalData = new LinkedHashMap<String, Object>();
}
How to serialization it to JSON using Jackson ObjectMapper ?
For example, suppose the optionalData is a Map saving two entries <"type", "book"> and <"year", "2014">
I want the output to be as follow. Please note that the key/value of optionalData could be changed on the fly (so, I cannot create a "static" Java object for this without using Map)
[
{
id: "book-id1",
type: "book",
year: "2014"
},
{
id: "book-id2",
type: "book",
year: "2013"
}
]
You can use the #JsonAnyGetter annotation on a getter method that returns the map of optional values. Please refer to this blog plost that explains that in details.
Here is an example:
public class JacksonAnyGetter {
public static class myClass {
final String Id;
private final Map<String, Object> optionalData = new LinkedHashMap<>();
public myClass(String id, String key1, Object value1, String key2, Object value2) {
Id = id;
optionalData.put(key1, value1);
optionalData.put(key2, value2);
}
public String getid() {
return Id;
}
#JsonAnyGetter
public Map<String, Object> getOptionalData() {
return optionalData;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<myClass> objects = Arrays.asList(
new myClass("book-id1", "type", "book", "year", 2013),
new myClass("book-id2", "type", "book", "year", 2014)
);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(objects));
}
}
Output:
[ {
"id" : "book-id1",
"type" : "book",
"year" : 2013
}, {
"id" : "book-id2",
"type" : "book",
"year" : 2014
} ]
You need to write your own Jackson JsonSerializer to create custom JSON string from Java object as per the need.
Here are the nice posts along with example
JSON Serializer & DeserializerHow do I use a custom Serializer with Jackson?
How Do I Write a Jackson JSON Serializer & Deserializer?
The same thing you can achieve using GSON JsonSerializer
Here are some examples
Serialize java object with GSON
GSON Serialiser Example - javacreed
Here is the code using GSON Serialiser
List<MyClass> list = new ArrayList<MyClass>();
MyClass myClass1 = new MyClass();
myClass1.setId("book-id1");
myClass1.getOptionalData().put("type", "book");
myClass1.getOptionalData().put("year", "2014");
list.add(myClass1);
MyClass myClass2 = new MyClass();
myClass2.setId("book-id2");
myClass2.getOptionalData().put("type", "book");
myClass2.getOptionalData().put("year", "2013");
list.add(myClass2);
class MyClassSerialiser implements JsonSerializer<MyClass> {
#Override
public JsonElement serialize(final MyClass obj, final Type typeOfSrc,
final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", obj.getId());
Map<String, String> optioanlData = obj.getOptionalData();
if (optioanlData.size() > 0) {
for (Map.Entry<String, String> entry : optioanlData.entrySet()) {
jsonObject.addProperty(entry.getKey(), entry.getValue());
}
}
return jsonObject;
}
}
String jsonString = new GsonBuilder().setPrettyPrinting().
.registerTypeAdapter(MyClass.class, new MyClassSerialiser()).create()
.toJson(list);
System.out.println(jsonString);
output:
[
{
"id": "book-id1",
"type": "book",
"year": "2014"
},
{
"id": "book-id2",
"type": "book",
"year": "2013"
}
]

How Jackson-Creating a JsonObject

I want to create a JsonObject like this:
{
Response: 200,
Lists: [
{
Test: "Math",
Result: "6",
Credit: "3"
},
{
Test: "C++",
Result: "10",
Credit: "6"
}
]
}
I know create this with lib org.json but with Jackson? i try to use
JsonNodeFactory nodeFactory = new JsonNodeFactory();
but i have this problem
The constructor JsonNodeFactory() is not visible
Make sure to use the latest version of Jackson. They moved from codehaus to FasterXML: http://wiki.fasterxml.com/JacksonHome.
You don't need to instantiate the factory. You can use the public static one: com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode root = factory.objectNode();
root.put("Response", 200);
ArrayNode list = factory.arrayNode();
list.add(...);
...
root.set("List", list);
Note that Jackson is a great library to map Java POJOs to JSON (and back). Rather than creating the JSON structure by hand, you can create Java classes that Jackson will serialize to JSON:
public class Item {
#JsonProperty("Test")
private String test;
#JsonProperty("Result")
private String result;
#JsonProperty("Credit")
private String credit;
}
public class Root {
#JsonProperty("Response")
private int response;
#JsonProperty("List")
private List<Item> list;
}
public static void main(String[] args) {
Root root = new Root();
...
String json = new ObjectMapper().writeValueAsString(root)
}
To create a JsonNode object use ObjectMapper. For example:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readValue(JSON_STRING, JsonNode.class)
Refer to the Jackson documentation for information.

Categories

Resources