developer tip

Jackson을 사용하여 JSON을 다형성 유형으로 역 직렬화-전체 예제에서 컴파일 오류가 발생합니다.

optionbox 2020. 12. 1. 07:55
반응형

Jackson을 사용하여 JSON을 다형성 유형으로 역 직렬화-전체 예제에서 컴파일 오류가 발생합니다.


다형성 JSON의 역 직렬화를 허용해야하는 프로그래머 Bruce의 튜토리얼을 통해 작업하려고합니다.

전체 목록은 여기에서 찾을 수 있습니다 Programmer Bruce tutorials (Great stuff btw)

처음 5 개를 아무 문제없이 진행했지만 마지막 부분 (예제 6)에 걸림돌이 발생했습니다. 물론 작업을 수행하는 데 정말 필요한 부분입니다.

컴파일 타임에 다음 오류가 발생합니다.

ObjectMapper 유형의 readValue (JsonParser, Class) 메소드는 인수 (ObjectNode, Class)에 적용 할 수 없습니다.

그리고 그것은 코드 덩어리로 인해 발생합니다.

  public Animal deserialize(  
      JsonParser jp, DeserializationContext ctxt)   
      throws IOException, JsonProcessingException  
  {  
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();  
    ObjectNode root = (ObjectNode) mapper.readTree(jp);  
    Class<? extends Animal> animalClass = null;  
    Iterator<Entry<String, JsonNode>> elementsIterator =   
        root.getFields();  
    while (elementsIterator.hasNext())  
    {  
      Entry<String, JsonNode> element=elementsIterator.next();  
      String name = element.getKey();  
      if (registry.containsKey(name))  
      {  
        animalClass = registry.get(name);  
        break;  
      }  
    }  
    if (animalClass == null) return null;  
    return mapper.readValue(root, animalClass);
  }  
} 

특히 라인으로

return mapper.readValue (root, animalClass);

누구든지이 문제를 겪은 적이 있습니까? 그렇다면 해결책이 있습니까?

누구든지 미리 감사드립니다. Jon D.


약속 한대로 주석을 사용하여 다형성 객체를 직렬화 / 역 직렬화하는 방법에 대한 예제를 넣겠습니다.이 예제는 Animal여러분이 읽고있는 튜토리얼 클래스에서 기반으로했습니다 .

먼저 Animal하위 클래스에 대한 Json 주석이있는 클래스입니다.

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Dog.class, name = "Dog"),

    @JsonSubTypes.Type(value = Cat.class, name = "Cat") }
)
public abstract class Animal {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

그런 다음 하위 클래스 DogCat.

public class Dog extends Animal {

    private String breed;

    public Dog() {

    }

    public Dog(String name, String breed) {
        setName(name);
        setBreed(breed);
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }
}

public class Cat extends Animal {

    public String getFavoriteToy() {
        return favoriteToy;
    }

    public Cat() {}

    public Cat(String name, String favoriteToy) {
        setName(name);
        setFavoriteToy(favoriteToy);
    }

    public void setFavoriteToy(String favoriteToy) {
        this.favoriteToy = favoriteToy;
    }

    private String favoriteToy;

}

보시다시피 Catand Dog에 대한 특별한 것은 없으며 abstract클래스 에 대해 아는 유일한 것은 클래스 Animal이므로 역 직렬화 할 때 다음 테스트에서 볼 수 있듯이를 대상으로 Animal하고 ObjectMapper실제 인스턴스를 반환합니다.

public class Test {

    public static void main(String[] args) {

        ObjectMapper objectMapper = new ObjectMapper();

        Animal myDog = new Dog("ruffus","english shepherd");

        Animal myCat = new Cat("goya", "mice");

        try {
            String dogJson = objectMapper.writeValueAsString(myDog);

            System.out.println(dogJson);

            Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);

            System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());

            String catJson = objectMapper.writeValueAsString(myCat);

            Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);

            System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());



        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Test클래스 실행 후 출력 :

{"@type":"Dog","name":"ruffus","breed":"english shepherd"}

Deserialized dogJson Class: Dog

{"@type":"Cat","name":"goya","favoriteToy":"mice"}

Deserialized catJson Class: Cat

도움이 되었기를 바랍니다,

호세 루이스


A simple way to enable polymorphic serialization / deserialization via Jackson library is to globally configure the Jackson object mapper (jackson.databind.ObjectMapper) to add information, such as the concrete class type, for certain kinds of classes, such as abstract classes.

To do that, just make sure your mapper is configured correctly. For example:

Option 1: Support polymorphic serialization / deserialization for abstract classes (and Object typed classes)

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 

Option 2: Support polymorphic serialization / deserialization for abstract classes (and Object typed classes), and arrays of those types.

jacksonObjectMapper.enableDefaultTyping(
    ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 

Reference: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization


You need only one line before the declaration of the class Animal for correct polymorphic serialization/deserialization:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class Animal {
   ...
}

This line means: add a meta-property on serialization or read a meta-property on deserialization (include = JsonTypeInfo.As.PROPERTY) called "@class" (property = "@class") that holds the fully-qualified Java class name (use = JsonTypeInfo.Id.CLASS).

So, if you create a JSON directly (without serialization) remember to add the meta-property "@class" with the desired class name for correct deserialization.

More information here


If using the fasterxml then,

these changes might be needed

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;

in main method--

use

SimpleModule module =
  new SimpleModule("PolymorphicAnimalDeserializerModule");

instead of

new SimpleModule("PolymorphicAnimalDeserializerModule",
      new Version(1, 0, 0, null));

and in Animal deserialize() function, make below changes

//Iterator<Entry<String, JsonNode>> elementsIterator =  root.getFields();
Iterator<Entry<String, JsonNode>> elementsIterator = root.fields();

//return mapper.readValue(root, animalClass);
return  mapper.convertValue(root, animalClass); 

This works for fasterxml.jackson. If it still complains of the class fields. Use the same format as in the json for the field names (with "_" -underscore). as this
//mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy()); might not be supported.

abstract class Animal
{
  public String name;
}

class Dog extends Animal
{
  public String breed;
  public String leash_color;
}

class Cat extends Animal
{
  public String favorite_toy;
}

class Bird extends Animal
{
  public String wing_span;
  public String preferred_food;
}

참고URL : https://stackoverflow.com/questions/30362446/deserialize-json-with-jackson-into-polymorphic-types-a-complete-example-is-giv

반응형