Полиморфная десериализация JSON с Джексоном с информацией о типе из родительского узла - PullRequest
0 голосов
/ 24 октября 2018

У меня есть этот 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 интерфейса, но не смогузнать, как получить доступ к соседнему узлу родительского элемента с помощью флага типа.

Знаете ли вы, как исправить иерархию классов и / или использовать функции / аннотации Джексона для десериализации JSON с типом "first_type" на FirstPower class и "second_type" на SecondPower?

Я использую Jackson 2.9.7 Можно немного изменить иерархию классов и формат JSON, а также у меня есть возможность использовать десериализацию на основе аннотаций.

1 Ответ

0 голосов
/ 24 октября 2018

Поскольку информация type хранится в классе Dto, пользовательский JsonDeserializer должен быть реализован для класса 'Dto' вместо интерфейса 'Power', чтобы получить доступ к информации type.Важной частью реализации пользовательского JsonDeserializer в приведенном ниже коде является строка

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

, где getPowerClass метод определяет класс (FirstPower или SecondPower), требуемый с помощью type из dto.Как только класс известен, мы можем десериализовать объект power, просто вызвав метод readValueAs.Следующие классы (должны быть помещены в один и тот же пакет) демонстрируют, как реализовать пользовательский JsonDeserializer.

Основной класс

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                   "
                + "            }                                           "
                + "          }                                             "
                + "        ]                                               "
                + "      }                                                 ";

    }
} 

Класс Dto

import java.util.Collection;

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

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

Класс DtoDeserializer

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);
    }
}

Интерфейс питания

public interface Power {}

FirstPower класс

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;
    }
}

SecondPower класс

public class SecondPower implements Power {
    boolean anotherParam;

    boolean isAnotherParam() {
        return anotherParam;
    }

    void setAnotherParam(boolean anotherParam) {
        this.anotherParam = anotherParam;
    }

    @Override
    public String toString() {
        return "anotherParam:" + String.valueOf(anotherParam);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...