Deserialize JSON to classes
Solution 1
I had to accomplish something very similar, here is an excerpt.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonSubTypes({
@JsonSubTypes.Type(value=IMetricCollection.class, name="MetricCollection"),
@JsonSubTypes.Type(value=IMetricDouble.class, name="MetricDouble"),
@JsonSubTypes.Type(value=IMetricInteger.class, name="MetricInteger"),
@JsonSubTypes.Type(value=IMetricPlot.class, name="MetricPlot"),
@JsonSubTypes.Type(value=IMetricString.class, name="MetricString"),
@JsonSubTypes.Type(value=IMetricMatrix.class, name="MetricMatrix")
})
public interface IMetric extends HasViolations<IViolation>, Serializable {
/**
* Getter for the name of the object.
*
* @return
*/
public abstract String getName();
/**
* Set the name of the object.
*
* @param name
*/
public abstract void setName(String name);
/**
* Returns true if metric has violations.
* @return
*/
public abstract boolean hasMetricViolations();
}
This may seem kind of counter intuitive for using an interface but I was able to get this all working by telling the interface what concrete class to use. I also have another chunk of code in a separate project that overrides the JsonSubTypes
to instantiate it's own type of classes below, if this helps.
@JsonDeserialize(as=MetricMatrix.class)
public interface IMetricMatrix<C extends IColumn> extends IMetric {
public static interface IColumn extends IMetricCollection<IMetric> {
}
public static interface IIntegerColumn extends IColumn {
}
public static interface IDoubleColumn extends IColumn {
}
public static interface IStringColumn extends IColumn {
}
public abstract List<C> getValue();
public abstract void setValue(List<C> value);
public abstract void addColumn(C column);
}
In this class I can parse the same REST message but I am overriding the original projects concrete types and the subtypes for this project make them persistent. Since the type names are the same I can override what interface to use for this object type. Please keep in mind that I am using the @class property but this is completely arbitrary could be @whatever annotation but it would need to match on both sides. This is not using the JsonTypeInfo.Id.Class
annotation.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonSubTypes({
@JsonSubTypes.Type(value=IMetricCollectionEntity.class, name="MetricCollection"),
@JsonSubTypes.Type(value=IMetricDoubleEntity.class, name="MetricDouble"),
@JsonSubTypes.Type(value=IMetricIntegerEntity.class, name="MetricInteger"),
@JsonSubTypes.Type(value=IMetricPlotEntityEntity.class, name="MetricPlot"),
@JsonSubTypes.Type(value=IMetricStringEntity.class, name="MetricString"),
@JsonSubTypes.Type(value=IMetricMatrixEntity.class, name="MetricMatrix")
})
public interface IMetricEntity extends IDatastoreObject, IMetric {
public String getContext();
public List<IViolation> getViolations();
}
@JsonDeserialize(as=MetricMatrixEntity.class)
public interface IMetricMatrixEntity extends IMetricEntity {
public static interface IColumnEntity extends IColumn {
public String getName();
}
public static interface IIntegerColumnEntity extends IColumnEntity {
}
public static interface IDoubleColumnEntity extends IColumnEntity {
}
public static interface IStringColumnEntity extends IColumnEntity {
}
public abstract List<IColumnEntity> getValue();
public abstract void setValue(List<IColumnEntity> value);
public abstract void addColumn(IColumnEntity column);
}
Solution 2
You should use a class, not an interface. Otherwise, Jackson cannot create an instance.
I believe you also need to create default (aka no-arg) constructors for your POJOs for Jackson to work.
Also, a good general approach for creating a Jackson mapping is to instantiate a Java instance of your classes and then create the JSON from that, Java -> JSON. This makes it much easier to understand how the mapping is different - going from JSON -> Java is harder to debug.
Eugen Martynov
My biggest challenge is bringing up mobile development to desktop/server side quality and processes level I'm interested in extending my knowledge and skills in writing clean, readable, maintainable code, as well as progress in quick continuous quality automated software releases I'm in love with Scrum, and I'm looking to extended relationship with Kanban Specialties: Android, iOS, BlackBeryy, J2ME, Java Mobile application development, unit testing, design patterns, continuous integration, automated testing, build management
Updated on June 15, 2020Comments
-
Eugen Martynov almost 4 years
Server returns such part of JSON:
{"condition": { "or": [ { "and": [ { "operand": "a", "operator": "==", "value": "true" }, { "not": { "operand": "b", "operator": "==", "value": "true" } } ] }, { "and": [ { "operand": "b", "operator": "==", "value": "true" }, { "not": { "operand": "a", "operator": "==", "value": "true" } } ] } ] }}
I wrote next classes hierarchy:
public interface Condition {} public class Expression implements Condition { public Expression(String operator, String operand, String value) { } } public class Not implements Condition { public Not(Condition condition) { } } public abstract class GroupOperation implements Condition { public GroupOperation (List<Condition> conditions) { } } public class And extends GroupOperation { public And(List<Condition> conditions) { } } public class Or extends GroupOperation { public Or(List<Condition> conditions) { } }
I've added next jackson annotations in hope to deserialize JSON above:
@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(value=Not.class, name="not"), @JsonSubTypes.Type(value=And.class, name="and"), @JsonSubTypes.Type(value=Or.class, name="or"), @JsonSubTypes.Type(value=Expression.class, name=""), })
I marked appropriate constructors as
@JsonCreator
.This doesn't work for
Expression
class.
If I modify JSON that every
expression
object has the name "expression":"expression" : { "operand": "a", "operator": "==", "value": "true" }
And
@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT) @JsonSubTypes({ @JsonSubTypes.Type(value=Not.class, name="not"), @JsonSubTypes.Type(value=And.class, name="and"), @JsonSubTypes.Type(value=Or.class, name="or"), @JsonSubTypes.Type(value=Expression.class, name="expression"), })
It fails when trying to parse "not" condition saying that "can't instantiate abstract class need more information about type". So looks like it loses annotations declaration in deeper parsing.
I wonder if it's possible to write deserialization with jackson for original JSON
Why second approach doesn't work for
Not
deserialization
-
Tom Carchrae over 11 yearsit would be helpful if you posted the actual class hierarchy - the code above does not look like it would compile
-
Eugen Martynov over 11 yearsModified code to be java code. The full source is here: github.com/emartynov/spil-games-assignment/tree/master/…
-
Visruth over 11 yearsCorrect thess lines : Not implement Condition to Not implements Condition public class And() extends to public class And extends public class Or() extends to public class Or extends
-
Eugen Martynov over 11 yearsTom, thank you for reply. Could you propose classes hierarchy from json example above which will be correctly de-serialized by Jackson?
-
Eugen Martynov over 11 yearsSee me comment to question please
-
Tom Carchrae over 11 yearsSorry - I'm really busy at the moment. I'd suggest making no-arg constructors to your classes as a first step. Also, work from the top of your hierarchy down. The simple class "public class Condtion { Map<String,Object> condition; }" should de-serialize by default, then inspect the result and slowly add more specific mappings for each element.
-
Eugen Martynov over 11 yearsHow to map
Expresion
class? It's unnamed object in json -
Eugen Martynov over 11 yearsChris, thank you for answer. Could you provide your json example or look at mine? It is almost working my code but with some modifications to json.
-
Tom Carchrae over 11 yearsYou don't need names for the object - if you are referring to these [{"operand":"a","operator": "==","value": "true"}] that can just be a List<Expression> expressions - where expression has three fields.
-
Eugen Martynov over 11 yearsTom, take a look on the json - "and" and "or" contains a list of Condition basically other "and", "or" as well "not" or simple Expression
-
Tom Carchrae over 11 yearssure - i saw that. you have disjunction/or at the top level, which contains a list of conjunctions/and at the next level, and then a list of expression in each conjunction. does it need to be more expressive than that? it would seem a badly designed api if you have to inspect the structure of the data to determine the type of something!
-
Eugen Martynov over 11 yearsSo you suggest me to remove interface Condition and have base real class Expression and all other operations should be inherited from it?