How to Deserialize json for class having Optional fields using Jackson - java

I'm using Jackson to deserialise a class which has Optional member variables, so it looks like
class Test{
Optional<String> testString;
}
but in serialised form it looks like, which is legit
{
"value": {
"testString": "hi"
}
How can I deserialise it back to my Test class?, because when I try to do so it says unknown field "value". Can it be possible without changing my test class.

You need to register Jdk8Module. Belowe you can find example, how to do that:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import java.io.File;
import java.util.Optional;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module());
Test test = new Test();
test.setTestString(Optional.of("str"));
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(test);
System.out.println(json);
System.out.println(mapper.readValue(json, Test.class));
}
}
Above code prints:
{
"testString" : "str"
}
Test{testString=Optional[str]}
See also:
jackson-modules-java8

Related

Deserialize json object which properties can be changed each time

I have the following json content:
{
"arguments": {
"key" : "value"
}
}
But, it can dynamically change depending on the specified configuration of the json file.
For example once can be like above, but sometimes it can be empty arguments: {} or something it may be configured something like this:
"arguments": {
"someKeyName" : "someValue"
}
or even:
"arguments": {
"someKeyName": "someKeyValue",
"someKeyName2": "someKeyValue2"
}
My question is, how can I handle the deserialization with Jackson's object mapper or some other alternative, without knowing what would the properties be of the arguments object each time I have to handle the deserialization ?
You can de-serialize it into a Map<String, Object> and you will always get a map but its internal content would be different each time. you can do something like this:
Map<String, Object> data = objectReader.forType(Map.class).readValue(jsonString);
BTW, I wrote my own JsonUtils class which is a thin wrapper over JSON-Jackson library that simplifies a bit serializing and deserializing JSON strings. reading a simple map would look like:
Map<String, Object> map = JsonUtils.readObjectFromJsonString("{\"a\": \"b\"}", Map.class);
But with my class you don't need to instantiate and configure ObjectMapper instance. Here is JsonUtils Javadoc. If you want to use it this class is part of Open source MgntUtils library written and maintained by me. You can get MgntUtils library as Maven artifact or on Github (including source code and Javadoc)
Assume (spring-boot-starter+):
package com.example.demo;
import java.util.HashMap;
import java.util.Map;
public class Dto {
final Map<String, Object> arguments = new HashMap<>();
public Map<String, Object> getArguments() {
return arguments;
}
}
..then we can (already) test:
package com.example.demo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class DemoJacksonApplicationTests {
#Autowired
ObjectMapper objectMapper;
#Test
void test1() throws JsonProcessingException {
Dto dto = objectMapper.readValue(
"""
{
"arguments": {
"key" : "value"
}
}
""", Dto.class);
assertEquals("value", dto.getArguments().get("key"));
}
#Test
void test2() throws JsonProcessingException {
Dto dto = objectMapper.readValue(
"""
{
"arguments": {
"someKeyName" : "someValue"
}
}
""", Dto.class);
assertEquals("someValue", dto.getArguments().get("someKeyName"));
}
#Test
void test3() throws JsonProcessingException {
Dto dto = objectMapper.readValue(
"""
{
"arguments": {
"someKeyName": "someKeyValue",
"someKeyName2": "someKeyValue2"
}
}
""", Dto.class);
assertEquals("someKeyValue", dto.getArguments().get("someKeyName"));
assertEquals("someKeyValue2", dto.getArguments().get("someKeyName2"));
}
}

Нow can i serialize #ConfigMapping

I have a project with 3 files in Quarkus
application.properties
conf.obj[0].name=name0
conf.obj[0].code=code0
conf.obj[0].versions[0].number=1
conf.obj[1].name=name1
conf.obj[1].code=code1
conf.obj[1].versions[0].number=1
conf.obj[2].name=name2
conf.obj[2].code=code2
conf.obj[2].versions[0].number=1
conf.obj[2].versions[1].number=2
AvailableConfig.java
package com.example;
import io.quarkus.runtime.annotations.StaticInitSafe;
import io.smallrye.config.ConfigMapping;
import java.util.List;
#StaticInitSafe
#ConfigMapping(prefix = "conf")
public interface AvailableConfig {
List<listObject> obj();
interface listObject {
String name();
String code();
List<Version> versions();
interface Version {
Integer number();
}
}
}
MainService.java
package com.example;
import io.quarkus.runtime.StartupEvent;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.json.bind.Jsonb;
import java.util.List;
#ApplicationScoped
public class MainService {
#Inject
AvailableConfig availableConfig;
#Inject
Jsonb jsonb;
void onStart(#Observes StartupEvent ev) {
List<AvailableConfig.listObject> config = availableConfig.obj();
String result = jsonb.toJson(config);
}
}
As a result of execution, the correct object "config" is created.
But when it is serialized, an empty json "[{},{},{}]" is obtained.
How do I properly serialize things like this?
I don't know why jsonb behaves like this, but I found several solutions:
1. use Gson
Gson gson = new Gson();
String resultGson = gson.toJson(config);
2. use jackson
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String resultJackson = objectMapper.writeValueAsString(config);
But I'm not sure if these options are ready for AOT-compilation in Quarkus environment.
3. Therefore, the best way is not to store such things in the config format, it is better to put them in your json-file.

JsonInclude.Include.NON_DEFAULT not working with custom serializer

I want to both use a custom serializer and have the JsonInclude.Include.NON_DEFAULT designation be honored. When I don't use a custom serializer it is honored but when I do use a custom serializer it is not.
This is Jackson 2.2.2. I do not presently have the option to switch to a newer version of Jackson.
Here's a simple example that shows the problem:
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
public class JacksonSerialization
{
public static void main(String[] args) throws Exception {
ObjectMapper serializer = new ObjectMapper();
Foo foo = new Foo();
foo.setFlags(EnumSet.of(Flag.CC, Flag.BB));
System.out.println(serializer.writeValueAsString(foo));
foo = new Foo();
System.out.println(serializer.writeValueAsString(foo));
}
public static enum Flag
{
AA,
BB,
CC
}
#JsonInclude(JsonInclude.Include.NON_DEFAULT)
public static class Foo
{
private Set<Flag> flags;
public Foo() {
flags = EnumSet.of(Flag.AA);
}
#JsonGetter("f")
#JsonSerialize(using = FlagSetSerializer.class)
public Set<Flag> getFlags() {
return flags;
}
public void setFlags(Set<Flag> theFlags) {
flags = theFlags;
}
}
public static class FlagSetSerializer extends JsonSerializer<Set<Flag>>
{
#Override
public void serialize(Set<Flag> value,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
String csv = value.stream()
.map(Flag::toString)
.collect(Collectors.joining(","));
jsonGenerator.writeString(csv);
}
}
}
And here's the output:
{"f":"BB,CC"}
{"f":"AA"}
Note that f is being serialized even when it has the default value. If I comment out the #JsonSerialize annotation then I get the following output:
{"f":["BB","CC"]}
{}
Then f properly does not get serialized. But of course things are not being serialized in the format I want.
So how do I get the custom serializer to honor the class's #JsonInclude annotation?
You probably want to implement public boolean isEmpty(SerializerProvider provider, T value) as per the documentation, which says:
public boolean isEmpty(SerializerProvider provider, T value)
Method called to check whether given serializable value is considered
"empty" value (for purposes of suppressing serialization of empty
values).
Default implementation will consider only null values to be
empty.
As per https://github.com/FasterXML/jackson-databind/issues/730
Another possible source of trouble is that you talk about NON_EMPTY but you code uses NON_DEFAULT.
And rather too much digging in the debugger leads me to suggest
#JsonSerialize(using = FlagSetSerializer.class, include = JsonSerialize.Inclusion.NON_DEFAULT)
Which seems to pass your tests.
The problem seems to be in JacksonAnnotationInspector#findSerializationInclusion, which first looks for a #JsonInclude attribute on the property, and when it fails to find that, it looks for a #JsonSerialize annotation. #JsonSerialize includes a deprecated include property, which has a default value of ALWAYS.
I've not looked into it too deeply, but I suspect a deprecation/refactor managed to slice off some functionality. C'est la vie.

How to prevent null values inside a Map and null fields inside a bean from getting serialized through Jackson

I have a a Map<String,Foo> foosMap that I want to serialize through Jackson . Now I want following two settings on the serialization process:
The Map can have have plenty of null values and null keys and I don't want nulls to be serialized.
For all those Foos that are getting serialized, I do not want to serialize null objects referenced inside Foo.
What is the best way to achieve this ? I am using jackson-core1.9 and jackson-mapper1.9 jars in my project.
If it's reasonable to alter the original Map data structure to be serialized to better represent the actual value wanted to be serialized, that's probably a decent approach, which would possibly reduce the amount of Jackson configuration necessary. For example, just remove the null key entries, if possible, before calling Jackson. That said...
To suppress serializing Map entries with null values:
Before Jackson 2.9
you can still make use of WRITE_NULL_MAP_VALUES, but note that it's moved to SerializationFeature:
mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
Since Jackson 2.9
The WRITE_NULL_MAP_VALUES is deprecated, you can use the below equivalent:
mapper.setDefaultPropertyInclusion(
JsonInclude.Value.construct(Include.ALWAYS, Include.NON_NULL))
To suppress serializing properties with null values, you can configure the ObjectMapper directly, or make use of the #JsonInclude annotation:
mapper.setSerializationInclusion(Include.NON_NULL);
or:
#JsonInclude(Include.NON_NULL)
class Foo
{
public String bar;
Foo(String bar)
{
this.bar = bar;
}
}
To handle null Map keys, some custom serialization is necessary, as best I understand.
A simple approach to serialize null keys as empty strings (including complete examples of the two previously mentioned configurations):
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
Map<String, Foo> foos = new HashMap<String, Foo>();
foos.put("foo1", new Foo("foo1"));
foos.put("foo2", new Foo(null));
foos.put("foo3", null);
foos.put(null, new Foo("foo4"));
// System.out.println(new ObjectMapper().writeValueAsString(foos));
// Exception: Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.getSerializerProvider().setNullKeySerializer(new MyNullKeySerializer());
System.out.println(mapper.writeValueAsString(foos));
// output:
// {"":{"bar":"foo4"},"foo2":{},"foo1":{"bar":"foo1"}}
}
}
class MyNullKeySerializer extends JsonSerializer<Object>
{
#Override
public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused)
throws IOException, JsonProcessingException
{
jsonGenerator.writeFieldName("");
}
}
class Foo
{
public String bar;
Foo(String bar)
{
this.bar = bar;
}
}
To suppress serializing Map entries with null keys, further custom serialization processing would be necessary.
For Jackson versions < 2.0 use this annotation on the class being serialized:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Answer seems to be a little old, What I did was to use this mapper to convert a MAP
ObjectMapper mapper = new ObjectMapper().configure(SerializationConfig.Feature.WRITE_NULL_MAP_VALUES, false);
a simple Map:
Map<String, Object> user = new HashMap<String,Object>();
user.put( "id", teklif.getAccount().getId() );
user.put( "fname", teklif.getAccount().getFname());
user.put( "lname", teklif.getAccount().getLname());
user.put( "email", teklif.getAccount().getEmail());
user.put( "test", null);
Use it like this for example:
String json = mapper.writeValueAsString(user);
my solution, hope help
custom ObjectMapper and config to spring xml(register message conveters)
public class PyResponseConfigObjectMapper extends ObjectMapper {
public PyResponseConfigObjectMapper() {
disable(SerializationFeature.WRITE_NULL_MAP_VALUES); //map no_null
setSerializationInclusion(JsonInclude.Include.NON_NULL); // bean no_null
}
}

How to make a fieldName to fieldValue map deserializing a json string

I have a class having trivial string typed fields and one map only:
class MyClass {
#SerializedName("handle");
String nickName;
Map randomDetails;
}
My requirement is to create a map of fieldName to fieldValue (Map) but the fieldNames should be the same as #SerializedName rather than Myclass's field name. I realize that for a complex type like MyClass I may have to do some low-level deserialization myself. Has anyone come across this?
If you use a library, you shouldn't need to do any low-level work.
I haven't used it (yet) but Jackson looks like it'll do what you need.
It would be especially easy if you're not required to use that #SerializedName annotation, as Jackson provides a suite of its own annotations which do exactly what you need - (see the #JsonProperty annotation).
If you use the Jackson Tree Model mode of operation, you should get something like the map-based results you're looking for.
(I think I understand that the question concerns how to use Gson to deserialize a JSON map structure to a Java Map.)
Gson currently needs a little bit more type information about the Map than the Java class structure in the original question provides. Instead of declaring that randomDetails is a plain old Map, let Gson know that it's a Map<String, String>. Then, the following example JSON and simple deserialization code runs as expected.
input.json Contents:
{
"handle":"the handle",
"random_details":{"one":1,"too":"B","3":false,"for":5.32}
}
Foo.java:
import java.io.FileReader;
import java.util.Map;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
public class Foo
{
public static void main(String[] args) throws Exception
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gson = gsonBuilder.create();
MyClass myObject = gson.fromJson(new FileReader("input.json"), MyClass.class);
System.out.println(gson.toJson(myObject));
}
}
class MyClass
{
#SerializedName("handle")
String nickName;
Map<String, String> randomDetails;
}
Note that this converts all values in the Map into Strings. If you wanted something more generic, like a Map<String, Object>, or if randomDetails must be a plain old Map without additional type information, then it's necessary to implement custom deserialization processing, as described in the user guide. (This is a situation where Gson unfortunately does not currently automatically generate Java values of String or primitive type from JSON primitives, if the declared Java type is simply Object. Thus it's necessary to implement the custom deserialization.)
Here's one such example.
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.annotations.SerializedName;
public class Foo
{
public static void main(String[] args) throws Exception
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(MyClass.class, new MyClassDeserializer());
Gson gson = gsonBuilder.create();
MyClass myObject = gson.fromJson(new FileReader("input.json"), MyClass.class);
System.out.println(gson.toJson(myObject));
}
}
class MyClassDeserializer implements JsonDeserializer<MyClass>
{
#Override
public MyClass deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException
{
JsonObject object = json.getAsJsonObject();
String nickName = object.get("handle").getAsString();
Set<Map.Entry<String, JsonElement>> mapEntries = object.get("random_details").getAsJsonObject().entrySet();
Map randomDetails = new HashMap(mapEntries.size());
for (Map.Entry<String, JsonElement> mapEntry : mapEntries)
{
String key = mapEntry.getKey();
Object value;
JsonPrimitive jsonPrimitive = mapEntry.getValue().getAsJsonPrimitive();
if (jsonPrimitive.isNumber()) value = jsonPrimitive.getAsNumber();
else if (jsonPrimitive.isBoolean()) value = jsonPrimitive.getAsBoolean();
else value = jsonPrimitive.getAsString();
randomDetails.put(key, value);
}
MyClass myObject = new MyClass();
myObject.nickName = nickName;
myObject.randomDetails = randomDetails;
return myObject;
}
}
class MyClass
{
#SerializedName("handle")
String nickName;
Map randomDetails;
}

Categories