[java] Jackson의 사용자 지정 deserializer에서 기본 deserializer를 어떻게 호출합니까?

Jackson의 사용자 지정 deserializer에 문제가 있습니다. 기본 직렬 변환기에 액세스하여 역 직렬화중인 개체를 채우고 싶습니다. 채우기 후에 몇 가지 사용자 지정 작업을 수행하지만 먼저 기본 Jackson 동작으로 개체를 역 직렬화하려고합니다.

이것이 제가 현재 가지고있는 코드입니다.

public class UserEventDeserializer extends StdDeserializer<User> {

  private static final long serialVersionUID = 7923585097068641765L;

  public UserEventDeserializer() {
    super(User.class);
  }

  @Override
  @Transactional
  public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {

    ObjectCodec oc = jp.getCodec();
    JsonNode node = oc.readTree(jp);
    User deserializedUser = null;
    deserializedUser = super.deserialize(jp, ctxt, new User()); 
    // The previous line generates an exception java.lang.UnsupportedOperationException
    // Because there is no implementation of the deserializer.
    // I want a way to access the default spring deserializer for my User class.
    // How can I do that?

    //Special logic

    return deserializedUser;
  }

}

필요한 것은 기본 디시리얼라이저를 초기화하여 특수 로직을 시작하기 전에 POJO를 미리 채울 수있는 방법입니다.

사용자 지정 deserializer 내에서 deserialize를 호출 할 때 serializer 클래스를 구성하는 방법에 관계없이 현재 컨텍스트에서 메서드가 호출되는 것 같습니다. 내 POJO의 주석 때문입니다. 이로 인해 명백한 이유로 스택 오버플로 예외가 발생합니다.

나는 초기화를 시도 BeanDeserializer했지만 프로세스가 매우 복잡하고 올바른 방법을 찾지 못했습니다. 또한 AnnotationIntrospector.NET 파일의 주석을 무시하는 데 도움이 될 수 있다고 생각하면서을 (를) 사용하지 않도록 오버로드 해 보았습니다 DeserializerContext. 마지막으로 JsonDeserializerBuildersSpring에서 응용 프로그램 컨텍스트를 확보하기 위해 몇 가지 마법 작업을 수행해야했지만 성공했을 수도 있습니다 . 예를 들어 JsonDeserializer주석 을 읽지 않고 역 직렬화 컨텍스트를 구성하는 방법과 같이 더 깨끗한 솔루션으로 이끌 수있는 모든 것을 감사하겠습니다 .



답변

StaxMan이 이미 제안했듯이을 작성하고을 BeanDeserializerModifier통해 등록하여이를 수행 할 수 있습니다 SimpleModule. 다음 예제가 작동합니다.

public class UserEventDeserializer extends StdDeserializer<User> implements ResolvableDeserializer
{
  private static final long serialVersionUID = 7923585097068641765L;

  private final JsonDeserializer<?> defaultDeserializer;

  public UserEventDeserializer(JsonDeserializer<?> defaultDeserializer)
  {
    super(User.class);
    this.defaultDeserializer = defaultDeserializer;
  }

  @Override public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException
  {
    User deserializedUser = (User) defaultDeserializer.deserialize(jp, ctxt);

    // Special logic

    return deserializedUser;
  }

  // for some reason you have to implement ResolvableDeserializer when modifying BeanDeserializer
  // otherwise deserializing throws JsonMappingException??
  @Override public void resolve(DeserializationContext ctxt) throws JsonMappingException
  {
    ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
  }


  public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
  {
    SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier()
    {
      @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
      {
        if (beanDesc.getBeanClass() == User.class)
          return new UserEventDeserializer(deserializer);
        return deserializer;
      }
    });


    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(module);
    User user = mapper.readValue(new File("test.json"), User.class);
  }
}


답변

나는 받아 들여진 대답보다 훨씬 더 읽기 쉬운 ans 에서 대답을 찾았다 .

    public User deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
            User user = jp.readValueAs(User.class);
             // some code
             return user;
          }

이보다 더 쉽지는 않습니다.


답변

에 사용할 수 DeserializationContext있는 readValue()방법이 있습니다. 이것은 기본 deserializer와 사용자 지정 deserializer 모두에서 작동합니다.

그냥 호출해야합니다 traverse()JsonNode당신이 검색 읽고 싶은 수준 JsonParser에 전달할 readValue().

public class FooDeserializer extends StdDeserializer<FooBean> {

    private static final long serialVersionUID = 1L;

    public FooDeserializer() {
        this(null);
    }

    public FooDeserializer(Class<FooBean> t) {
        super(t);
    }

    @Override
    public FooBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        FooBean foo = new FooBean();
        foo.setBar(ctxt.readValue(node.get("bar").traverse(), BarBean.class));
        return foo;
    }

}


답변

이를 수행하는 몇 가지 방법이 있지만 올바르게 수행하려면 약간의 작업이 필요합니다. 기본적으로 기본 디시리얼라이저에 필요한 정보는 클래스 정의에서 빌드되기 때문에 하위 클래스를 사용할 수 없습니다.

따라서 가장 많이 사용할 수있는 것은를 생성하고 인터페이스 BeanDeserializerModifier를 통해 등록하는 것입니다 Module(사용 SimpleModule). 정의 / 재정의해야하며 modifyDeserializer, 고유 한 논리 (유형이 일치하는 위치)를 추가하려는 특정 경우에 대해 고유 한 deserializer를 생성하고 제공된 기본 deserializer를 전달해야합니다. 그런 다음 deserialize()메서드 에서 호출을 위임하고 결과 Object를 가져올 수 있습니다.

또는 실제로 개체를 만들고 채워야하는 경우 그렇게하여 deserialize()세 번째 인수 를 사용 하는 오버로드 된 버전을 호출 할 수 있습니다 . 역 직렬화 할 개체입니다.

작동 할 수있는 또 다른 방법 (100 % 확실하지는 않음)은 Converter객체 ( @JsonDeserialize(converter=MyConverter.class)) 를 지정하는 것 입니다. 이것은 새로운 Jackson 2.2 기능입니다. 귀하의 경우 Converter는 실제로 유형을 변환하지 않고 객체를 간단하게 수정합니다.하지만 기본 deserializer가 먼저 호출되고 Converter.


답변

추가 User 클래스를 선언 할 수있는 경우 주석을 사용하여 구현할 수 있습니다.

// your class
@JsonDeserialize(using = UserEventDeserializer.class)
public class User {
...
}

// extra user class
// reset deserializer attribute to default
@JsonDeserialize
public class UserPOJO extends User {
}

public class UserEventDeserializer extends StdDeserializer<User> {

  ...
  @Override
  public User deserialize(JsonParser jp, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = jp.ReadValueAs(UserPOJO.class);
    return deserializedUser;

    // or if you need to walk the JSON tree

    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    JsonNode node = oc.readTree(jp);
    // specify UserPOJO.class to invoke default deserializer
    User deserializedUser = mapper.treeToValue(node, UserPOJO.class);

    return deserializedUser;
  }

}


답변

무엇의 라인을 따라 토마시 Záluský 제안했다 사용하는 경우, BeanDeserializerModifier사용 자신 디시리얼라이저 기본을 구성 할 수 있습니다 바람직하지 않다 BeanDeserializerFactory필요한 몇 가지 추가 설정이 있지만,. 맥락에서이 솔루션은 다음과 같습니다.

public User deserialize(JsonParser jp, DeserializationContext ctxt)
  throws IOException, JsonProcessingException {

    ObjectCodec oc = jp.getCodec();
    JsonNode node = oc.readTree(jp);
    User deserializedUser = null;

    DeserializationConfig config = ctxt.getConfig();
    JavaType type = TypeFactory.defaultInstance().constructType(User.class);
    JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, type, config.introspect(type));

    if (defaultDeserializer instanceof ResolvableDeserializer) {
        ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
    }

    JsonParser treeParser = oc.treeAsTokens(node);
    config.initialize(treeParser);

    if (treeParser.getCurrentToken() == null) {
        treeParser.nextToken();
    }

    deserializedUser = (User) defaultDeserializer.deserialize(treeParser, context);

    return deserializedUser;
}


답변

다음은 ObjectMapper를 사용하는 oneliner입니다.

public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    OMyObject object = new ObjectMapper().readValue(p, MyObject.class);
    // do whatever you want 
    return object;
}

그리고 제발 : 정말 어떤 문자열 값이나 다른 것을 사용할 필요가 없습니다. 필요한 모든 정보는 JsonParser에서 제공하므로 사용하십시오.