使用来自父节点的类型信息的jackson对JSON进行多态反序列化

时间:2018-10-23 23:42:15

标签: java json jackson deserialization

我有这个JSON文档

1:
{
  "type": "first_type",
  "configs": [
    {
      "itemLevel": 1,
      "power": {
        "firstTypeParam": "xxxx"
      }
    },
    {
      "itemLevel": 2,
      "power": {
        "firstTypeParam": "yyy"
      }
    }
  ]
}

2:
{
  "type": "second_type",
  "configs": [
    {
      "itemLevel": 11,
      "power": {
        "anotherParam": true
      }
    },
    {
      "itemLevel": 12,
      "power": {
        "anotherParam": false
    }
  ]
}

几个Java类

public class Dto {
  String type;
  Collection<Config>; 
}

public class Config {
  int itemLevel;
  Collection<Power> powers; 
}

public interface Power {}

public class FirstPower implements Power {
  String firstTypeParam;
}

public class SecondPower implements Power {
  boolean anotherParam;
}

我尝试在@JsonDeserialize(using = MyStdDeserializer.class"界面的顶部实现自定义杰克逊解串器Power,但是找不到如何访问带有类型标志的父节点的邻居节点。

您是否知道如何修复类层次结构和/或使用杰克逊功能/注释将"first_type"类型的FirstPower类型和"second_type"类型的SecondPower反序列化JSON?< / p>

我正在使用杰克逊2.9.7 可以稍微更改类层次结构和JSON格式,而且我也可以使用基于注释的反序列化。

1 个答案:

答案 0 :(得分:1)

由于type信息存储在Dto类中,因此应为'Dto'类而不是'Power'接口实现自定义JsonDeserializer,以便访问{{1 }}信息。以下代码中实现自定义type的关键部分是

  

config.powers.add(parser.readValueAs(getPowerClass(dto.type)));

其中JsonDeserializer方法使用getPowerClass中的FirstPower确定所需的类(SecondPowertype)。一旦知道了该类,就可以简单地通过调用dto方法来反序列化power对象。以下类(应放在同一包中)演示了如何实现自定义readValueAs

主班

JsonDeserializer

Dto类

import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class PolymorphicDeserialize {
    public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();

        Dto type1 = mapper.readValue(getType1Json(), Dto.class);
        Dto type2 = mapper.readValue(getType2Json(), Dto.class);

        printDto(type1);
        printDto(type2);
    }

    private static void printDto(Dto dto) {
        System.out.println("type :" + dto.type);
        for (Config config : dto.configs) {
            System.out.println("itemLevel:" + config.itemLevel);
            System.out.println("powers:" + config.powers);
        }
    }

    private static String getType1Json() {

        return "   {                                                       "
                + "        \"type\": \"first_type\",                       "
                + "        \"configs\": [                                  "
                + "          {                                             "
                + "            \"itemLevel\": 1,                           "
                + "            \"power\": {                                "
                + "              \"firstTypeParam\": \"xxxx\"              "
                + "            }                                           "
                + "          },                                            "
                + "          {                                             "
                + "            \"itemLevel\": 2,                           "
                + "            \"power\": {                                "
                + "              \"firstTypeParam\": \"yyy\"               "
                + "            }                                           "
                + "          }                                             "
                + "        ]                                               "
                + "      }                                                 ";

    }

    private static String getType2Json() {

        return "   {                                                       "
                + "        \"type\": \"second_type\",                      "
                + "        \"configs\": [                                  "
                + "          {                                             "
                + "            \"itemLevel\": 11,                          "
                + "            \"power\": {                                "
                + "              \"anotherParam\": true                    "
                + "            }                                           "
                + "          },                                            "
                + "          {                                             "
                + "            \"itemLevel\": 12,                          "
                + "            \"power\": {                                "
                + "              \"anotherParam\": false                   "
                + "            }                                           "
                + "          }                                             "
                + "        ]                                               "
                + "      }                                                 ";

    }
} 

DtoDeserializer类

import java.util.Collection;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(using = DtoDeserializer.class)
public class Dto {
    String type;
    Collection<Config> configs;
}

电源接口

import java.io.IOException;
import java.util.ArrayList;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

public class DtoDeserializer extends JsonDeserializer<Dto> {

    @Override
    public Dto deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        Dto dto = new Dto();
        dto.configs = new ArrayList<Config>();
        while (parser.nextToken() == JsonToken.FIELD_NAME) {
            deserializeType(parser, dto);
            deserializeConfigs(parser, dto);
        }
        return dto;
    }

    private void deserializeType(JsonParser parser, Dto dto) throws IOException, JsonProcessingException {
        if (!"type".equals(parser.getCurrentName())) {
            return;
        }
        parser.nextToken();
        dto.type = parser.getValueAsString();
    }

    private void deserializeConfigs(JsonParser parser, Dto dto) throws IOException, JsonProcessingException {
        if (!"configs".equals(parser.getCurrentName())) {
            return;
        }
        if (parser.nextToken() != JsonToken.START_ARRAY) {
            return;
        }
        while (parser.nextValue() != null) {
            if (parser.getCurrentToken() != JsonToken.START_OBJECT) {
                continue;
            }
            Config config = new Config();
            config.powers = new ArrayList<Power>();
            while (parser.nextToken() != JsonToken.END_OBJECT) {
                if ("itemLevel".equals(parser.getCurrentName())) {
                    parser.nextToken();
                    config.itemLevel = parser.getValueAsInt();
                } else if ("power".equals(parser.getCurrentName())) {
                    parser.nextToken();
                    config.powers.add(parser.readValueAs(getPowerClass(dto.type)));
                }
            }
            dto.configs.add(config);
        }
    }

    private Class<? extends Power> getPowerClass(String type) {
        if ("first_type".equals(type)) {
            return FirstPower.class;
        } else if ("second_type".equals(type)) {
            return SecondPower.class;
        }
        throw new IllegalArgumentException("Not known type" + type);
    }
}

FirstPower类

public interface Power {}

SecondPower类

public class FirstPower implements Power {
    String firstTypeParam;

    String getFirstTypeParam() {
        return firstTypeParam;
    }

    void setFirstTypeParam(String firstTypeParam) {
        this.firstTypeParam = firstTypeParam;
    }

    @Override
    public String toString() {
        return "firstTypeParam:" + firstTypeParam;
    }
}