I'm looking to replace Jackson deserialization with Boon to test the differences in deserialization speeds. I am reading JSON from a file (which can be in the millions of lines long), consisting of multiple blocks that will each represent a POJO instance (MyPojo.java)and storing these instances in a Collection. I also have a custom deserializer that will omit the creation of certain POJOs. At the minute I have the following in Jackson:
public Collection<MyPojo> load()
{
ObjectMapper mapper = new ObjectMapper().registerModule(new MyCustomDeserializer());
return mapper.readValue(jsonFile, new TypeReference<Collection<MyPojo>>(){});
}
I know that the Boon API mimics Jacksons so I tried:
ObjectMapper boonMapper = JsonFactory.create();
return boonMapper.readValue(jsonFile, new TypeReference<Collection<MyPojo>>(){});
...but it doesn't seem to like this, it can't find the method that accepts these types.
Forgetting the registering the custom deserializer for now (that'll be my next problem), is this type of deserialization, straight to a Collection, supported in Boon?
Do the following;
return boonMapper.readValue(jsonFile, List.class, MyPojo.class);
Related
Given class:
#Data
class Widget {
String name;
int price;
}
And the following yaml file:
Widget1:
price: 5
Widget2:
price: 6
Widget3:
price: 7
Using Jackson I want to deserialize this into a Map<String, Widget> where the widget name field is set to the corresponding map key. The following snippet works but has the downside of preventing use of immutable object types such as lombok #Value. Another inelegant solution I considered was creating a separate immutable WidgetWithName class that is constructed after jackson deserialization. If someone can propose a better approach, that would be of interest.
Map<String, Widget> getWidgets(String yaml) throws Exception {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Widget.class);
map = mapper.readValue(yaml, mapType);
map.forEach((k, v) -> v.setName(k); // so much for immutability
return map
}
Jackson will not help you because it is a generalization over YAML and JSON (and XML or so I heard) and therefore provides less functionality than directly using SnakeYAML.
With SnakeYAML, you could theoretically implement this with custom constructors, but it would need a lot of knowledge about the deserialization process because YAML is not designed to merge nodes on different levels into one value.
Frankly, having a separate class is by far the simplest and best maintainable solution. A separate class named WidgetBuilder, with only the price field and a function Widget finish(String name) would allow you to
return map.entrySet().stream().map((k, v) -> v.finish(k)).collect(
Collectors.toMap(Widget::getName, Function.identity()));
which seems elegant enough. (You can collect to a list instead if you don't need the map anymore.)
I want to map between simple object to protobuff using object mapper
when i tried this it cause an exception
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.convertValue(enterprise, EnterpriseMessage.Enterprise.class);
Exception message was: cannot find a (map) key deserializer for type simple type
In my opinion objectmapper is not the best option to map protos
since objectmapper is used to map JSON into POJO's and vice versa.
My recomendation for this purpose is using mapstruct which provides
a wide functionality to map java beans. Specially between protos and POJO's.
Just by creating an interface mapper for the class you want to map the framework
generates the implementacion.
I write you an example that you can follow.
import org.mapstruct.Mapper;
#Mapper
public interface EnterpriseProtoMapper {
EnterpriseMessage.Enterprise toProto(Enterprise enterprise);
}
For further information, you can check mapStruct's documentation in this link:
MapStruct 1.3.1.Final Reference Guide
I am using Spring Data Redis in order to cache some data using #Cacheable. I have multiple types of objects that need to be cached and I need the data from Redis to be in JSON format. I know that, by default, the serializer used is JdkSerializationRedisSerializer, but with is the cached data is not human readable.
I order to save the data in JSON format I wanted to use GenericJackson2JsonRedisSerializer and I've created a custom ObjectMapper too:
public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory cf) {
ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder().failOnEmptyBeans(false)
.failOnUnknownProperties(false)
.indentOutput(false)
.serializationInclusion(JsonInclude.Include.NON_NULL)
.modules(
// Optional
new Jdk8Module(),
// Dates/Times
new JavaTimeModule()
)
.featuresToDisable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS,
SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS
).build();
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(cf);
redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
Using this RedisTemplate doesn't work and I always get back this error:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to <some class>
As I understood, when deserializing, Jackson doesn't know the type of the specific object since it's Object and creates a LinkedHashMap to hold the data. Maybe I am wrong with this, but how can I achieve saving the cached data as JSON for multiple types of objects with #Cacheble?
You are right, you can achieve saving multiple types with GenericJackson2JsonRedisSerializer. But in your example, you provide custom ObjectMapper to GenericJackson2JsonRedisSerializer and GenericJackson2JsonRedisSerializer doesn't configure Jackson's default typing for you (you can check default constructor logic in GenericJackson2JsonRedisSerializer). You have to tune it on your own and add this to your ObjectMapper
objectMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
This will include type information to JSON. Like this
{
"#class": "com.example.Foo",
"field": "bar"
}
Since 2.10 ObjectMapper better to use
activateDefaultTyping(mapper.polymorphicTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL)
I'm trying to generate a JSON string from a complex java object (using Jackson API). While parsing a field I see ClassCastException. The Java objects are not owned by my project so cannot change and fix the issue. Is there any easy way to fix this?
Please note, my code deals with any kind of Java object and doesn't this Java object in particular so I'm looking for something generic where if a field is not parsed successful, just ignore and move to the next one.
ObjectMapper mapper = new ObjectMapper();
CustomModule module = new CustomModule();
mapper.registerModule(module);
ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
ow.writeValueAsString(value)
You can globally disable checking for instance :
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
By default Jackson throws an exception, if it encounters a JSON property that it can not bind to object property.
Good Day,
Hi All,
I'm trying to configure the Jackson Object Mapper to de-serialize any json String to my domain specific objects.
I've configured the Object Mapper as below
ObjectMapper mapper = new ObjectMapper();
mapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "remoteClass");
In my domain, any json string should contain a remoteClass property which denotes the actual JAVA POJO (canonical name) that it corresponds to. The above configuration helps resolve abstract types
However with the above, now java.util.Lists are not getting properly deserialized, since they are abstract types.
I get the following error
Unexpected token (START_OBJECT), expected VALUE_STRING: need JSON String that contains type id (for subtype of java.util.List)
Object Mapper in this case is not resorting to the default collectionFallBacks
I tried other configuration to overcome the issue such as
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addAbstractTypeMapping(List.class, ArrayList.class);
mapper.registerModule(module);
However these lead to other problems.
Can someone please help me with the same. I've spent days on this now. This seems like a limitation in Jackson. Correct me if I'm wrong
I faced this problem recently. Try this, in your POJO, instead of defining property as
List<SimpleModule>
define it as
List<Object>.