morphia handle bad data - java

Let's say I have some json like this in mongo:
{"n":"5"}
and a java class like this:
#Entity
public class Example {
Integer n;
}
This works (I know that the json should store the value as an int not a string but I don't control that part).
Now when I have data like this morphia throws:
{"n":""}
I'm looking for a workaround (the behavior I'd like is for empty string to be treated same as null).
The only workaround I have so far is:
public class Example {
String n;
public Integer getN() {
return NumberUtils.isNumber(n) ? NumberUtils.createInteger(n) : null;
}
}
But I'm hoping for some way to hang an annotation on the Integer property that customizes the deserialization behavior.

So I asked this on the morphia google group and I thought I'd share the answer. Using the lifecycle annotation #PreLoad allows me to modify the DBObject before conversions into POJO takes place. So this should do it:
#PreLoad void fixup(DBObject obj) {
if (StringUtils.isEmpty(obj.get("n"))) {
obj.put("n",null);
}
}

Related

Spring Boot MongoDB Persistance MappingException: Cannot convert Java.util.ArrayList into an instance of class java.lang.Object

tl;dr
Atempting to add an ArrayList in which Object may be an ArrayList to Persistance.
Tried to add an AttributeConverter > Failed
Plz Help
I have no idea what I am doing.
How stupid am I?
The Problem
Dependencies
spring-boot-starter-data-jpa 2.0.0
spring-boot-starter-data-mongodb 2.0.0
eclipselink 2.7.1 <- Probably don't need this one, not sure.
So here is my problem I am trying to add persistence in a Spring Boot Application for a MongoDB in this case I am using tables, the problem comes exactly on the TableRaw bean (a striped down version of Table just for persistance).
Document(collection = "rule_tables")
public class TableRaw {
#Id
private String _id;
private String key;
private String name;
private String returns;
private ArrayList<AxisRaw> axis;
private ArrayList<Object> values = new ArrayList<>();
}
Everything else is just the default constructor (without _id) and getsetters.
So everything works fine with the exception of the values ArrayList. It works fine if it just a simple ArrayList with number and whatnot however in my case I want something like what I am inserting into the database (this is done every time it runs for testing purposes and the values inserted are using the MongoRepository, it works fine)
{
"_id":"5ac20c8b8ee6e6360c8947be",
"key":"1",
"name":"Table 1",
"returns":"Number",
"axis":[
{
"name":"potato",
"values":[
{
"_id":"BottomEdge","value":0
},{
"_id":"Range",
"value":[1,2]
},{
"_id":"TopEdge",
"value":3
}
]
}
],
"values":[
[1,2,3],
[1,2,3],
[1,2,3]
],
"_class":"pt.i2s.gm.gm.rulehandler.tables.model.TableRaw"
}
(For usage in the code the axis length and number of axis matters but in this case it is completely irrelevant.)
Anyway as stated previously it inserts fine into MongoDB but when attempting to get the value the following error is presented.
org.springframework.data.mapping.MappingException: Cannot convert [1, 2, 3] of type class java.util.ArrayList into an instance of class java.lang.Object! Implement a custom Converter<class java.util.ArrayList, class java.lang.Object> and register it with the CustomConversions. Parent object was: [empty]
First thing first I don't exactly know what Parent object was: [empty] means.
Second I tried creating an AttributeConverter as such:
#Component
#Converter(autoApply = true)
public class ArrayList2ObjectConverter implements
AttributeConverter<ArrayList<Object>,Object> {
#Override
public Object convertToDatabaseColumn(ArrayList<Object> attribute) {
return attribute;
}
#SuppressWarnings("unchecked") //If you don't like it suppress it
#Override
public ArrayList<Object> convertToEntityAttribute(Object dbData) {
System.out.println("Converting...");
return (ArrayList<Object>)dbData;
}
}
And adding #Convert(converter = ArrayList2ObjectConverter.class) above the values attribute. However this wasn't even called.
For some reason I couldn't find any answers to this problem, possibly due to my bad coding and making something that is just stupid to do so nobody would do it like this cause it doesn't work.
So how do I do this? And thank you for reading.
Update regarding the Axis and Value amounts
thomi sugested something that would work if I knew from the get go what type of values the table added. I apreciate the answere however some clarification should be made regarding this.
I do not know how many Axis, and therefore nested arrays I will have, it may be 1 it may be 30.
I do not know what the class type of objects will be, it may be numbers, Strings, Booleans, dates, etc. the options are limited but still extensive.
Possible Solution Which I Do Not don't want to use
I could simply create an Object that held a string and an ArrayList which would probably work fine, however I wanted to avoid this resolution, as I don't want to add irrelevant information to the database.
Adopted Solution
By request of #user_531 I will add the solution to this problem.
As this was not working I altered my aproach to the utilization of a new object called ValueList which is simply a wrapper class for a single Object
private ArrayList<ValueList> values;
ValueList Class
public class ValueList {
public Object value;
}
This allows me to add any type of object I want to the list, this does result however in tables looking like this:
{
"key":1,
...... (Same as above)
"values": [
{
"value": [
{
"value":1
},
{
"value":2
}
]
},
{
"value": [
{
"value":3
},
{
"value":4
}
]
}
]
}
Which does look hidious but it doesn't fail anymore and allows me to read values relativelly consistently by calling the "getValue()" method or "getValueList()" method acording to the result from "isValueList()".
I think you should not map something to an object. In your DB, you will surely have an idea of what datatype there will be in your Array, In your case, try and replace with:
#Document(collection = "rule_tables")
public class TableRaw {
#Id
private String _id;
private String key;
private String name;
private String returns;
private ArrayList<AxisRaw> axis;
private List<List<Integer>> values; // no initialization.
}
This should map your structure just fine.

Serializing a POJO based on a runtime whitelist of properties

Is it possible to serialize a whitelisted subset of a POJO's properties (where the whitelist is known only at runtime) using Jackson?
All the solutions I know of so far (Views, #JsonIgnoreProperties etc.) are static, compile-time solutions.
Further, my backend returns results in the following format:
{
"outcome": "SUCCESS", // an enum
"message": "Success.", // a message for the developer
"result": {
// Some result that's different for each call
}
}
So I am looking for a solution that can be applied to only parts of the object graph (like the contents of the result property).
You probably want to look at #JsonFilter.
See this tutorial on serializing only fields that meet some criteria which includes details of this, and a couple of other methods.
For completeness
#JsonFilter("pojo-filter")
class Pojo {
public int foo;
}
FilterProvider filters = new SimpleFilterProvider()
.addFilter("pojo-filter", new SimpleBeanPropertyFilter() {
#Override
protected boolean include(PropertyWriter writer) {
return "foo".equals(writer.getName())
? Random.nextBoolean()
: true;
}
});
new ObjectMapper().writer().filters(filters).write(new Pojo());
Globally you can use ObjectMapper.setFilterProvider

Include enum type to serialize and deserialize with jackson

I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}

Augment jackson's JsonParser

Currently my applications can do as follows. It can read a JSON configuration such as:
{
"a": 5,
"b": 3
}
Into a POJO that looks like:
public class AddConf {
private Number a;
private Number b;
// constructor, getters and setters
public int add() {
return a.intValue() + b.intValue();
}
}
Then we can call the add function a return a result of 8 in this example. I would like to augment com.fasterxml.jackson.databind somehow to allow my JSON to have placeholders. So let's say when my configuration I don't know what value b will be until runtime, then I might make a configuration like this:
{
"a": 5,
"b": $b_placeholder$
}
To signify that b value will be provided at runtime. Obviously the above is not a valid JSON, and by default jackson throws an Exception (as it should) when it attempts to parse this with ObjectMapper's readValue. Ideally, I would like to read the above "JSON" (or something equivalent) into a POJO that looks something like this:
public class AddConf {
private Map<String,String> usedPlaceholders;
private Number a;
private Number b;
// constructor, getters and setters
public int add(Map<String,String> runtimeConf) {
if (usedPlaceholders.contains("a")) { // if "a" was a placeholder
a = runtimeConf.get(usedPlaceholder.get("a"));
}
if (usedPlaceholders.contains("b")) { // if "b" was a placeholder
b = runtimeConf.get(usedPlaceholder.get("b"));
}
return a.intValue() + b.intValue();
}
public void setPlaceholder(String key, String value) {
usedPlaceholder.put(key, value);
}
}
The idea is when deserializing the augmented JSON above, it would call setPlaceholder("b", "b_placeholder") instead of setting the value b and therefore when add is called, it will use values in the passed in runtimeConf Map instead of values from the JSON to do its configuration.
Given this, I have 2 questions:
Is there an easier way to accomplish my goal of having "placeholders" in my JSON configuration? It seems if I was to implement my idea here, I would have to override some of the Jackson classes. I would have to override the com.fasterxml.jackson.core.JsonParser to allow $ as a valid token in some scenarios, I would also have to write custom derserializers for all my configuration POJO (such as AddConf). This would likely casade into me having to override much of the jackson code base, which I would rather not do.
If I were to take this approach to override some of the default jackson classes, how might I go about doing that?
I am currently using jackson 2.6.0
You may be looking for annotation #JsonRawValue, use of which allows specifying EXACT contents to include as value, while still taking care of adding necessary separators.

How do I convert XML into a java value object?

What kind of open-source libraries are available to convert XML into a java value object?
In .Net, there is a way to easily do this with xml serialization and attributes. I would imagine there some parallel in java. I know how to do this with a DOM or SAX parser, but I was wondering if there was an easier way.
I have a predefined XML format that looks something like this.
<FOOBAR_DATA>
<ID>12345</ID>
<MESSAGE>Hello World!</MESSAGE>
<DATE>22/04/2009</DATE>
<NAME>Fred</NAME>
</FOOBAR_DATA>
In .Net, I can do something like this to bind my object to the data.
using System;
using System.Xml.Serialization;
namespace FooBarData.Serialization
{
[XmlRoot("FOOBAR_DATA")]
public class FooBarData
{
private int _ID = 0;
[XmlElement("ID")]
public int ID
{
get { return this._ID; }
set { this._ID = value; }
}
private string _Message = "";
[XmlElement("MESSAGE")]
public string Message
{
get { return this._Message; }
set { this._Message = value; }
}
private string _Name = "";
[XmlElement("NAME")]
public string Name
{
get { return this._Name; }
set { this._Name = value; }
}
private Date _Date;
[XmlElement("DATE")]
public Date Date
{
get { return this._Date; }
set { this._Date= value; }
}
public FooBarData()
{
}
}
}
I was wondering if there was a method using annotations, similar to .Net or perhaps Hibernate, that will allow me to bind my value object to the predefined-XML.
I like XStream alot, especially compared to JAXB - unlike JAXB, XStream doesn't need you to have an XSD. It's great if you have a handful of classes you want to serialize and deserialize to XML, without the heavy-handed-ness of needing to create a XSD, run jaxc to generate classes from that schema, etc. XStream on the other hand is pretty lightweight.
(Of course, there are plenty of times where JAXB is appropriate, such as when you have a pre-existing XSD that fits the occasion...)
JAXB allows you to convert an XML Schema (XSD) file into a collection of Java classes. This may be more "structured" than the XMLEncoder/Serializable approach that Andy's (excellent, by the way) answer provides.
Java has an XMLEncoder that you might be able to use to serialize an object to XML. The only requirement is that your object implements "Serializable."
Here's an example:
http://www.developer.com/java/web/article.php/1377961
JiBX is another option.
For more opinions on Java-to-XML data binding, see XML serialization in Java?

Categories

Resources