Java Model:
#lombok.Data
public class Foo {
...
private boolean isDefault;
}
Serialized to JSON:
{
...,
"isDefault" : true
}
ObjectMapper configuration:
ObjectMapper mapper = new ObjectMapper();
VisibilityChecker<?> vc = objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE)
.withIsGetterVisibility(JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(vc);
Question: the code cannot deserialize the JSON to java object and fails with UnrecognizedPropertyException for isDefault even though it perfectly serializes it to JSON. Any thought?
Thanks in advance.
Related
For our service input, we use an object mapper with certain configs to serialize it. We provide the same object mapper configs to our client in our client-lib and use the same to deserialize the input at our end.
Now we are adding another object to our input which is owned by a common team and has it's own object mapper config to correctly serialize it.
class MyAPIRequest {
MyOtherOwnedClass1 obj1;
MyOtherOwnedClass2 obj2;
//New Shared class which is being added as part of input now:
CommonlyOwnedClass newObj;
}
class MyAPIRequestObjectMapperFactory() {
static ObjectMapper newInstance(IonSystem ionSystem) {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
return objectMapper;
}
}
class CommonlyOwnedClassObjectMapperFactory() {
static ObjectMapper newInstance(IonSystem ionSystem) {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.ANY);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.ANY);
return objectMapper;
}
}
How can I update my ObjectMapper to use a different ObjectMapper (provided by CommonlyOwnedClassObjectMapperFactory) for newObj and continue to use existing object mapper (in MyAPIRequestObjectMapperFactory) for rest of objects in MyAPIRequest ?
Edit :
I am using Jackson-2.8, but can upgrade to 2.9 if required
The setVisibility method is controlled by an protected configuration variable (_serializationConfig & _deserializationConfig in 2.8 and _configOverrides in 2.9). The method setVisibility is overloaded which takes an visibility checker to override internal configuration variable. You can use the overloaded version to set configuration for your mapper by getting the visibility checker from external mapper.
ObjectMapper yourObjectMapper = MyAPIRequestObjectMapperFactory.newInstance();
ObjectMapper externalObjectMapper = CommonlyOwnedClassObjectMapperFactory.newInstance();
yourObjectMapper.setVisibility(externalObjectMapper.getVisibilityChecker());
//then set your visibility
I'm using spring boot 2.0.3 and
spring-boot-starter-data-redis.
Also using jackson-datatype-jsr310.
I want to store Object into redis.
the object(MyObj):
String text;
Instant instant;
Here's my code:
#Test
public void test() {
ListOperations<String, MyObj> listOps = redisTemplate.opsForList();
MyObj o1 = new MyObj();
o1.setText("foo");
o1.setInstant(Instant.now());
listOps.leftPush("foo", o1);
MyObj o2 = new MyObj();
o2.setText("bar");
o2.setInstant(Instant.now());
listOps.leftPush("foo", o2);
List<MyObj> list = listOps.range("foo", 0, -1);
for (MyObj o : list) {
System.out.println(o.getText());
System.out.println(o.getInstant());
}
}
in my RedisConfig:
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
But when I'm pushing into redis, the error occurs below:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of java.time.Instant (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
How to serialize java instant type with Redis?
Any opinion would be appreciated.
While this is quite an old post, I ran into this problem recently and did the lazy man search and found this before deciding to read the class file. I found that you can easily override the default ObjectMapper with a custom one. Use the setObjectMapper(ObjectMapper objectMapper) method on the Serializer to override the default.
// Taken from Jackson library
public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private final JavaType javaType;
private ObjectMapper objectMapper = new ObjectMapper();
// truncated
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
You just need to create an ObjectMapper with the JavaTime.Module registered like below
public static ObjectMapper dateMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
Jackson2JsonRedisSerializer<MyObj> valueSerializer = new
Jackson2JsonRedisSerializer<>(MyObj.class);
valueSerializer.setObjectMapper(dateMapper());
}
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) turns of default behavior.
I'm trying to (de)serialize an object that has a property with a type that comes from a maven dependency, so I can't change the class of this type.
The class of this type has a #JsonSerialize and #JsonDeserialize annotation.
However, I want to use the default serializer and deserialzer, because the custom serializer writes an array instead of an object. Is there a way, using annotations, to tell jackson to use the default (de)serializer?
You can disable the annotations using Jackson's mixins feature.
In the following example, any attempt at deserializing to a CustomerObj will result in an exception due to its defective Builder:
#JsonDeserialize(builder = CustomerObj.class)
public class CustomerObj {
public String name;
public int age;
public CustomerObj build() {
throw new RuntimeException("JsonDeserializer invoked");
}
}
Create a mixin with a JsonDeserialize annotation that disables the broken builder:
#JsonDeserialize(builder = java.lang.Void.class)
public static abstract class CustomerMixin { }
Register the mixin on the ObjectMapper instance:
ObjectMapper om = new ObjectMapper();
om.addMixIn(CustomerObj.class, CustomerMixin.class);
Enjoy working deserialization:
final String json = "{\"name\":\"Brian\",\"age\":41}";
CustomerObj customer = om.readValue(json, CustomerObj.class);
I am using Jersey with Jackson as JSON provider. I am able to serialize ZonedDateTime to JSON but when I want to deserialize it gives me error as follows.
Could you please help me tell the exact configuration required to get this deserialization work.
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.ZonedDateTime] from String value ('2016-01-21T21:00:00Z'); no single-String constructor/factory method
My mapper configuration is as follows:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper MAPPER;
public ObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
//This would add JSR310 (Datetime) support while converting date to JSON using JAXRS service
MAPPER.registerModule(new JavaTimeModule());
//Below line would disable use of timestamps (numbers),
//and instead use a [ISO-8601 ]-compliant notation, which gets output as something like: "1970-01-01T00:00:00.000+0000".
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}
I found the problem, actually issue was not with Deserialization using standard jackson provider. In my case, I was using Jersey client to get JSON and then deserialize using readEntity method.
Problem was that jersey client was not aware of the jsr310 module, so by registering the contextresolver where jsr310 has been added solved the issue. So in nutshell, you don't need to do anything for seralization and deserialization of ZonedDateTime if using normal jackson provider.
Below is the reference code which I am referring here, to get better clarity.
public class RESTClientImpl{
/*
* ***This is very important, JacksonJsonProvider is the implementation of
* MessageBodyWriter/Reader which is required for "readEntity" method,
* else it would throw MessageBodyWriter/Reader not found exception
*
* https://jersey.java.net/documentation/latest/message-body-workers.html#mbw.ex.client.mbr.reg
*
* Registering of ObjectMapperContextResolver is important as we have registered JSR310 module there and without registering this,
* Jersey client is not aware of JSR310 module, so it will not be able to de-serialize ZonedDateTime
*/
private final Client client = ClientBuilder.newClient(new ClientConfig().register(LoggingFilter.class)).register(JacksonJsonProvider.class)
.register(ObjectMapperContextResolver.class);
public User get(URI uri) {
WebTarget webTarget = client.target(uri);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
User user = response.readEntity(User.class);
return user;
}
}
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper MAPPER;
public ObjectMapperContextResolver() {
MAPPER = new ObjectMapper();
//This would add JSR310 (Datetime) support while converting date to JSON using JAXRS service
MAPPER.registerModule(new JavaTimeModule());
//Below line would disable use of timestamps (numbers),
//and instead use a [ISO-8601 ]-compliant notation, which gets output as something like: "1970-01-01T00:00:00.000+0000".
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Object.class, new ZonedDateTimeDeserializer());
MAPPER.registerModule(simpleModule);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return MAPPER;
}
}
I'm trying to parse some JSON using Jackson. Here is the JSON:
{
"data": {
"item1": "Hello",
"item2": "World"
}
}
I've seen implementations using Jackson where the data field is ignored/not read but the inner elements are still read and stored. I was wondering how this is achieved?
Cheers!
in your mapper configuration you can Unwrap the root element as follows
private ObjectMapper rootMapper()
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
return mapper;
}
for more detail on this you can look jackson data binder topic