Deserialize JSON to Java using Jackson 2 - java

I have a JSON:
{"evaluationPart": {
"generatedId": "48D5181DB8704F8AB5FC998964AD9075",
"evaluationQuestionPartOption": {
"generatedId": "48D5181DB8704F8AB5FC998964AD9075"
}
}}
I've created java classes for it to represent it:
The root class:
#JsonIgnoreProperties(value = {"evaluationPart"})
public class JsonEvaluationPart {
#JsonProperty("generatedId")
private String generatedId;
#JsonProperty("evaluationQuestionPartOption")
private JsonQuestionOption questionOption;
public String getGeneratedId() {
return generatedId;
}
public void setGeneratedId(String generatedId) {
this.generatedId = generatedId;
}
public JsonQuestionOption getQuestionOption() {
return questionOption;
}
public void setQuestionOption(JsonQuestionOption questionOption) {
this.questionOption = questionOption;
}
}
And the JsonQuestionOption class:
public class JsonQuestionOption {
#JsonProperty("generatedId")
private String generatedId;
public String getGeneratedId() {
return generatedId;
}
public void setGeneratedId(String generatedId) {
this.generatedId = generatedId;
}
}
I have written a small JUnit test to check how it goes:
public class JsonReaderTest {
/**
* Logger for this class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(JsonReaderTest.class);
private ObjectMapper objectMapper;
private static final String JSON = "{\"evaluationPart\": {\n" +
" \"generatedId\": \"48D5181DB8704F8AB5FC998964AD9075\",\n" +
" \"evaluationQuestionPartOption\": {\n" +
" \"generatedId\": \"48D5181DB8704F8AB5FC998964AD9075\"\n" +
" }\n" +
"}}";
#Before
public void setUp()
throws Exception {
LOGGER.debug("Creating the object mapper.");
objectMapper = new ObjectMapper();
LOGGER.debug("Object mapper successfully created. {}", objectMapper);
}
#Test
public void testJsonReader()
throws Exception {
JsonEvaluationPart partType = objectMapper.readValue(JSON, JsonEvaluationPart.class);
assertNotNull(partType);
LOGGER.debug("Part: {}.", partType);
assertEquals(partType.getGeneratedId(), "48D5181DB8704F8AB5FC998964AD9075");
assertEquals(partType.getQuestionOption().getGeneratedId(), "48D5181DB8704F8AB5FC998964AD9075");
}
}
The problem is that when I am reading my JSON like this:
JsonEvaluationPart partType = objectMapper.readValue(JSON, JsonEvaluationPart.class);
All the properties in partType are null. What I am doing wrong here and how to solve this?

According to the documentation JsonIgnoreProperties means:
Annotation that can be used to either suppress serialization of properties
(during serialization), or ignore processing of JSON properties read (during
deserialization).
Just try replacing:
#JsonIgnoreProperties(value = {"evaluationPart"})
with:
#JsonTypeName("evaluationPart")

Your Json Headers is wrong.
use
#JsonTypeName("evaluationPart")
instead of
#JsonIgnoreProperties(value = {"evaluationPart"})

Related

Deserialize array of strings to csv

I have a json payload with:
"host_names": [
"www.host1.com",
"www.host2.com"
]
How can I deserialize this as a csv using Jackson - e.g.:
"www.host1.com,www.host2.com"
Previously I was deserializing this as a String[] but I can't persist that with hibernate, so now I'm trying to deserialize it as a csv.
EDIT:
I want to use annotations to turn the json array into a string, where each element of the array is separated by a comma. Perhaps something like #JsonRawValue. The goal is to then persist the value to a data via hibernate.
public static void main(String[] args) {
String jsonStr = "{\"host_names\": [\r\n" +
" \"www.host1.com\",\r\n" +
" \"www.host2.com\"\r\n" +
"]}";
JSONObject jsonObject = new JSONObject(jsonStr);
JSONArray hostNames = jsonObject.getJSONArray("host_names");
String result = "";
for(int i=0;i<hostNames.length(); i++) {
if(!result.isEmpty())
result = result+",\""+hostNames.getString(i)+"\"";
else
result = "\""+hostNames.getString(i)+"\"";
}
System.out.println(result);
}
result
"www.host1.com","www.host2.com"
Other approach based on annotation
Create a class
class Server{
#JsonProperty(value = "host_names")
private List<String> hostNames;
public List<String> getHostNames() {
return hostNames;
}
public void setHostNames(List<String> hostNames) {
this.hostNames = hostNames;
}
}
Now use com.fasterxml.jackson.databind.ObjectMapper to parse the JSON into this class
public static void main(String[] args) throws JsonMappingException, JsonProcessingException {
String jsonStr = "{\"host_names\": [\r\n" +
" \"www.host1.com\",\r\n" +
" \"www.host2.com\"\r\n" +
"]}";
ObjectMapper mapper = new ObjectMapper();
Server server = mapper.readValue(jsonStr, Server.class);
System.out.println(server.getHostNames());
}
output
[www.host1.com, www.host2.com]
You can slightly modify your setter / getter for host_names property as
Bean Class
public class Server {
#JsonProperty("host_names")
private List<String> hostNames;
#JsonGetter("host_names")
public String getHostNames() {
return String.join(",", hostNames);
}
#JsonSetter("host_names")
public void setHostNames(List<String> hostNames) {
this.hostNames = hostNames;
}
}
Main Method
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Server server = mapper.readValue("{\"host_names\":[\"www.host1.com\",\"www.host2.com\"]}", Server.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(server);
System.out.println(json);
}
Output
{
"host_names" : "www.host1.com,www.host2.com"
}
This way you can serialize as comma separated string, and array of string while deserializing.
Another Solution using #JsonValue Annotation.
Define your bean class as:
public class Server {
#JsonProperty("host_names")
private List<String> hostNames;
#JsonValue
public String hostNames() {
return String.join(",", hostNames);
}
public List<String> getHostNames() {
return hostNames;
}
public void setHostNames(List<String> hostNames) {
this.hostNames = hostNames;
}
}
Main Method
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Server server = mapper.readValue("{\"host_names\":[\"www.host1.com\",\"www.host2.com\"]}", Server.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(server);
System.out.println(json);
}
Output
"www.host1.com,www.host2.com"
This method will only be useful if your bean class has no other fields to serialize.

Java, Jackson, Custom deserializer with extended classes

Using Jackson 2.10., I am trying to write a custom deserializer for a base class, but I have to deserialize fields with unknown field name. Then there are extended class that can also extend this serializer.
I have tried to use the #AnyGetter, and #AnySetter to accomplish it, and it kind of does work. Now I am just wondering if there is a way to do it through a custom deserializer.
I could do it with a base class, but it fails when some class extends it.
Here is the sample of what I have done.
The following is just the base class and its serializer and how I used in in the main.
//BaseClass
#JsonDeserialize(using = BaseClassDeserializer.class)
public static class BaseClass {
private ObjectNode customFields = JsonNodeFactory.instance.objectNode();
private int baseInt;
public int getBaseInt() {
return baseInt;
}
public void setBaseInt(int baseInt) {
this.baseInt = baseInt;
}
public JsonNode getCustomFields() {
return customFields;
}
public void setCustomFields(ObjectNode customFields) {
this.customFields = customFields;
}
public void putCustomFields(String key, JsonNode node) {
this.customFields.set(key, node);
}
}
// BaseClassDeserializer
public static class BaseClassDeserializer extends StdDeserializer<BaseClass> {
public BaseClassDeserializer() {
this(null);
}
public BaseClassDeserializer(Class<?> vc) {
super(vc);
}
#Override
public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
BaseClass result = new BaseClass();
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
result.setBaseInt((Integer) ((IntNode) node.get("baseInt")).numberValue());
node.fieldNames();
Iterator<String> iterator = node.fieldNames();
while (iterator.hasNext()) {
String fieldName = iterator.next();
if (!"baseInt".equals(fieldName)) {
result.putCustomFields(fieldName, node.get(fieldName));
}
}
return result;
}
}
// main
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
BaseClass myClass = mapper.readValue(json, BaseClass.class);
}
By going looking through the debugger, the fields are successfully loaded.
Now I am trying to extend BaseClass
// ExtendedClass
public static class ExtendedClass extends BaseClass {
#JsonProperty("extendedString")
private String extendedString;
public String getExtendedString() {
return extendedString;
}
public void setExtendedString(String extendedString) {
this.extendedString = extendedString;
}
}
public static void main(String[] args) throws JsonProcessingException {
String json = "{\n"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
ExtendedClass myClass = mapper.readValue(json, ExtendedClass.class);
}
And this crashes with a
BaseClass cannot be cast to ExtendedClass exception.
I am guessing I have to pass along the deserialization to the child class' deserializer, but I cannot figure out how.
In your deserialiser you always return object of type BaseClass and it can not be cast to ExtendedClass. You need to implement type recognition feature in your deserialiser. In your case, returned type depends from properties JSON payload contains. If JSON payload contains extendedString property you know you need to return ExtendedClass in other case just return BaseClass. Your deserialiser could look like below:
class BaseClassDeserializer extends StdDeserializer<BaseClass> {
public BaseClassDeserializer() {
super(BaseClass.class);
}
#Override
public BaseClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectNode root = jsonParser.readValueAsTree();
List<String> names = getNames(root);
BaseClass result = findAndInitCustomType(names, root);
result = initBase(names, result, root);
initCustomFields(names, root, result);
return result;
}
private void initCustomFields(List<String> names, ObjectNode root, BaseClass result) {
for (String name : names) {
result.putCustomFields(name, root.get(name));
}
}
private BaseClass findAndInitCustomType(List<String> names, ObjectNode root) {
final String extendedString = "extendedString";
if (names.contains(extendedString)) {
ExtendedClass result = new ExtendedClass();
result.setExtendedString(root.get(extendedString).asText());
names.remove(extendedString);
return result;
}
// else - check other custom fields for another types.
// if not available return null
return null;
}
private BaseClass initBase(List<String> names, BaseClass baseClass, ObjectNode root) {
if (baseClass == null) {
baseClass = new BaseClass();
}
final String baseInt = "baseInt";
if (names.contains(baseInt)) {
baseClass.setBaseInt(root.get(baseInt).asInt());
names.remove(baseInt);
}
return baseClass;
}
private List<String> getNames(ObjectNode root) {
List<String> names = new ArrayList<>();
root.fieldNames().forEachRemaining(names::add);
return names;
}
}
Example usage:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
String baseJson = "{"
+ "\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\""
+ "}";
String extendedJson = "{"
+ "\t\"baseInt\": 1,\n"
+ "\t\"customObject\" : {\n"
+ "\t\t\"key\": \"value\"\n"
+ "\t},\n"
+ "\t\"customString\" : \"STRING\",\n"
+ "\t\"extendedString\" : \"STRING\"\n"
+ "}";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(baseJson, BaseClass.class));
System.out.println(mapper.readValue(extendedJson, BaseClass.class));
System.out.println(mapper.readValue(extendedJson, ExtendedClass.class));
}
}
Above code prints:
BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
ExtendedClass{extendedString='STRING'} BaseClass{customFields={"customObject":{"key":"value"},"customString":"STRING"}, baseInt=1}
Improvements:
In BaseClass class instead of ObjectNode use Map<String, JsonNode> or even Map<String, Object>. It is not a good idea to tie POJO classes with 3-rd party libraries.
You do not need to use #JsonProperty annotation if you manually handle deserialisation.

Special characters in JSON property

I have JSON which has colon (:) in property. Now I am wondering how to map this property name with java class property.
I tried #JsonProperty("viacom:VideoDuration") but it is not working.
Here is the sample json
{
"shortTitle": "Teen Wolf",
"viacom:VideoDuration": "20h:30m",
"viacom:metadataLanguage": "en",
"viacom:contentType": "franchise",
"viacom:urlKey": "http://urlkey.com",
"viacom:vmid":"cee71f4a-ec7d-4ccd-a10d-9bf6b7506d352",
"viacom:originLanguage":"en"
}
Note : I do not have option to rename the json property name to something else.
Probably it's a problem of a library you are using or its version.
I've just created a simple test and it has ran properly.
Test class:
public class TestClass {
private String valueStr;
#JsonProperty("test:prop")
private String testProp;
public String getValueStr() {
return valueStr;
}
public void setValueStr(String valueStr) {
this.valueStr = valueStr;
}
public String getTestProp() {
return testProp;
}
public void setTestProp(String testProp) {
this.testProp = testProp;
}
}
And test:
#Test
public void test()
throws JsonParseException, JsonMappingException, IOException {
String test = "{\r\n" +
" \"test:prop\": \"Teen Wolf\",\r\n" +
" \"valueS\": \"franchise\"\r\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
TestClass data = mapper.readValue(test, TestClass.class);
Assert.assertTrue("expected Teen Wolf, actual=" + data.getTestProp(),
Objects.equals("Teen Wolf", data.getTestProp()));
}
I've used com.fasterxml.jackson.databind.ObjectMapper from com\fasterxml\jackson\core\jackson-databind\2.8.8\jackson-databind-2.8.8.jar
By the way which type the "viacom:VideoDuration" property is in the java class? I mean what if the problem not in the property name but in the value? It also has a colon and you are trying to deserialize it in the wrong way?

JSON deserialize property including $

Having a JSON with a property like this which i try to deserialze with jackson:
{ "$fooid" : "yfdfjjhkjhkljhd768622323jjj" }
Problem here is, i'm not able to read the key "$fooid".
I tried to annotate the property with
#JsonAlias("$fooid")
private String fooid;
also as
#JsonProperty("$fooid")
private String fooid;
Both variants won't work, the property fooid is always null.
How to deserialize it in Java? Any hints?
I am not sure how are you doing It, but It is working for me on version 2.0.0 doing this.
public class JSonAliasWithSpecialCharacters {
public static void main(String[] args) throws IOException {
String json="{ \"$fooid\" : \"yfdfjjhkjhkljhd768622323jjj\" }";
ObjectMapper mapper = new ObjectMapper();
JsonFooid fooid = mapper.readValue(json, JsonFooid.class);
System.out.println("read the foodid:"+fooid.getFooid());
}
}
public class JsonFooid {
#JsonProperty("$fooid")
private String fooid;
public String getFooid() {
return fooid;
}
public void setFooid(String fooid) {
this.fooid = fooid;
}
}

Mapping json string to interace with anonymous class

I have an interface, which I want to use for serialize/deserialize. I want to omit some of the fields. Code below is not working so far.
#JsonAutoDetect(fieldVisibility = Visibility.NONE)
public interface MyWrapper {
//no annotation to not serialize
String getMyField();
//annotation to deserialize
#JsonProperty("my_field")
void setMyField();
}
You can either specify #JsonIgnore annotation on the method, or #JsonIgnoreProperties(value = {"myfield"}) annotation on the class.
see examples here
EDIT:
which version of Jackson are you using? becuase in the one I am using (2.5) the use of #JsonIgnore together with #JsonProperty works perfectly.
also, notice that the setter needs to receive an argument to actually be used by Jackson
interface with fixed setter:
#JsonAutoDetect(fieldVisibility = Visibility.NONE)
public interface MyWrapper {
#JsonIgnore
String getMyField();
// annotation to deserialize
#JsonProperty("my_field")
void setMyField(String f);
}
implementation (nothing exciting here)
public class Foo implements MyWrapper {
private String myField;
public Foo() {}
public Foo(String f) {
setMyField(f);
}
#Override
public String getMyField() {
return myField;
}
#Override
public void setMyField(String f) {
myField = f;
}
}
testing :
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
// serialization - ignore field
try {
MyWrapper w = new Foo("value");
String json = mapper.writeValueAsString(w);
System.out.println("serialized MyWrapper: " + json);
} catch (Exception e) {
e.printStackTrace();
}
// de-serialization - read field
String json = "{\"my_field\":\"value\"}";
try (InputStream is = new ByteArrayInputStream(json.getBytes("UTF-8"))) {
MyWrapper w = (MyWrapper)mapper.readValue(is, Foo.class);
System.out.println("deserialized MyWrapper: input: " + json + " ; w.getMyField(): " + w.getMyField());
} catch (Exception e) {
e.printStackTrace();
}
}
output:
serialized MyWrapper: {}
deserialized MyWrapper: input: {"my_field":"value"} ; w.getMyField(): value

Categories

Resources