简体   繁体   中英

Deserializing nested / recursive JSON into Java objects with inheritance

I have a json which is complex/nested. My json file consists of two equivalent java objects. One is Complex_Expression and another is Simple_Expression . Complex_Expression is in the following form:

{
    "SomeOpearator":0,
    "ASpecificKey":1, //the value 1 is fixed for complex expression.
    "Expressions":[ ] //array of one or more expressions
}

Simple Expression is in the following form:

{
    "Operand":"Some String Value",
    "ASpecificKey":0, //the value 0 is fixed for simple expressions.
    "SomeComparisionOpearator":1, // enums which associates int to different comparison operators.
    "Value":["String1"] //this is an array of strings.
}

The Expressions in turn can have Complex_Expression and/or Simple_Expression . The json file always starts from Complex_Expression . I have to deserialize this JSON file. My final goal is to make an expression using Complex_Expression and Simple_Expression objects and with some logics in these classes. I don't mind using jackson or gson or maybe other dependencies.

Till now I have created a base class called Expression . Complex_Expression and Simple_Expression both inherits this class. Then I started writing Custom Json Deserializer . But in the custom deserializer I am stuck and I don't know how should I proceed. Please help me on this. My Simple_Expression class looks like this and somewhat similar is the Complex_Expression class.

public class Simple_Expression extends Expression
{
    @JsonProperty("Operand") //use jackson deserializer for this class.
    public String Operand;

    @JsonProperty("SomeComparisionOpearator")
    public SomeComparisionOpearator someCompareOperator;

    @JsonProperty("Value")
    public Object value;

    public Simple_Expression()
    {
        super(ASpecificKey.Simple); //Simple corresponds to 0
    }
}

Update

Some more description about my input and output. With input given a JSON string like this:

{
  "SomeOpearator": 0,
  "ASpecificKey": 1,
  "Expressions": [
    {
      "SomeOpearator": 1,
      "ASpecificKey": 1,
      "Expressions": [
        {
          "Operand": "People",
          "ASpecificKey": 0,
          "SomeComparisionOpearator": 14,
          "Value": [
            "Rich"
          ]
        }
      ]
    },
    {
      "SomeOpearator": 1,
      "ASpecificKey": 1,
      "Expressions": [
        {
          "Operand": "Grade",
          "ASpecificKey": 0,
          "SomeComparisionOpearator": 2,
          "Value": [
            "Grade A"
          ]
        }
      ]
    }
  ]
}

I should be able to do something like this, assuming jackson deserializer:

ObjectMapper mapper = new ObjectMapper();
Expression myExpressionObject = mapper.convertValue(jsonString, Expression.class);

It should give me the deserialized object into the myExpressionObject which will consists of a list of expressions (Arraylist or Array, no problem).

This would be easy with Gson extras RuntimeTypeAdapterFactory because you have the field ASpecificKey that can be used as type discriminator. See this for usage. You can just copy the source to your project if you already have Gson included.

I took a liberty to fix your Java naming convention so classes actually look like (and also your JSON should be fixed to correct convention):

@Getter @Setter
public class Expression {
    private int aSpecificKey;
}

@Getter @Setter
public class SimpleExpression extends Expression {
    public SimpleExpression() {
        setASpecificKey(0);
    }
    private String operand;
    private int someComparisonOperator;
    private String[] values; 
}

@Getter @Setter
public class ComplexExpression extends Expression {
    public ComplexExpression() {
        setASpecificKey(1);
    }   
    private String someOperator;
    private Expression[] expressions;
}

Against this kind of DTOs you could just instantiate a specific RuntimeTypeAdapterFactory :

final RuntimeTypeAdapterFactory<Expression> expressionTypeFactory = 
    RuntimeTypeAdapterFactory               
        .of(Expression.class, "aSpecificKey")
        .registerSubtype(SimpleExpression.class, "0")
        .registerSubtype(ComplexExpression.class, "1")

Then deserializing would be like:

Expression e = getGson(expressionTypeFactory)
        .fromJson(getPackageResourceReader(YOUR_NOTATION_FIXED_JSON),
                                               Expression.class);

Note that Gson will not by default deserialize the type discriminator aSpecificKey that is why there is default constructors that set that. If you do not need it you can remove the default constructors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM