내 CustomDeserializer 클래스는 동일한 클래스의 두 번째 필드에 사용되는 두 번째 시간을 작동하지 않습니다

user10713028 :

내가 잭슨 종속성을 사용하고, 나는 jsonParser 더 세 번 호출 될 때 문제라고 생각합니다. 하지만 난 그게 아니라면 그 일이 일어나고 있는지 왜 해요 .... 나는이 사건을 가지고 :

@Entity 
public class Car implements Serializable {

   @JsonDeserialize(using = CustomDeserialize.class)
   private Window windowOne:

   @JsonDeserialize(using = CustomDeserialize.class)
   private Window windowSecond:
   ....//Getters/Setters


}

CustomDeserializer 클래스

public class CustomDeserializer extends StdDeserializer<Window> {

  .....  // constructors


@Override
public Window deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
    JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
    String field = jsonParser.nextFieldName();
    String nextField = jsonParser.nextFieldName();
      return new Window("value1", "valu2");
    }
}

objectMapper를 호출 관리자 클래스

 public class Manager {

    private ObjectMapper mapper = new ObjectMapper();


    public void serializeCar(ObjectNode node) {
         // node comes loaded with valid values two windows of a Car.
         // All is OK until here, so this treeToValue enters to CustomDeserializer once only.
         // treeToValue just read the first window ?  because of second window is null and the first window enters on mode debug. 
         Car car = mapper.treeToValue(node, Car.class);
     }

 }

내가 디버깅 할 때 treeToValue (objectNode, 클래스) 단지 CustomSerializer 클래스에 한 번 호출하고 두 번째 시간을 호출하지 않는 이유를 모르겠어요. 여기에 무슨 제발? 또는 왜 mapper.treeToValue는 CustomDeserializer을 사용하여 두번째 필드는 무시? 미리 감사드립니다, 전문가.

업데이트]

나는 예로서 저장소를 추가 :

https://github.com/NextSoftTis/demo-deserializer

라니 :

귀하의 디시리얼라이저가 제대로 작동하지 않습니다.

당신이 도달하면 windowOne을 - 당신은 다음 두 필드의 이름을 읽고 "windowSecond"그리고 null(우리가 토큰 밖으로있어 이후) - 대신의 값으로 JsonNode는 읽었습니다. 시리얼 라이저 반환, 잭슨은 다음의 더 이상 토큰 및 건너 뜀 deserialisation가 없음을 볼 때 windowSecond 소비 더 이상 데이터가 없기 때문입니다.

@Override
public Window deserialize(JsonParser jsonParser, DeserializationContext dc) throws IOException, JsonProcessingException {
    JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
    String field = jsonParser.nextFieldName();
    String nextField = jsonParser.nextFieldName();
    return new Window(field + nextField, jsonNode.getNodeType().toString());
}

당신은 당신의 예제 프로그램의 출력을 보면이를 볼 수 있습니다

{
    "windowOne": {
        "value1": "windowSecondnull",
        "value2": "OBJECT"
    },
    "windowSecond": null
}

(샘플의 repo는 그런데 여기에 게시 동일한 코드가 포함되어 있지 않습니다).

선:

String field = jsonParser.nextFieldName();
String nextField = jsonParser.nextFieldName();

문제가, 당신은 사용해야입니다 JsonNode대신 읽은하고 예상대로 작동합니다 :

@Override
public Window deserialize(JsonParser jsonParser, DeserializationContext dc) throws IOException, JsonProcessingException {
    JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
    String value1 = jsonNode.hasNonNull("value1") ? jsonNode.get("value1").asText() : null;
    String value2 = jsonNode.hasNonNull("value2") ? jsonNode.get("value2").asText() : null;
    return new Window(value1, value2);
}

응답:

{
    "windowOne": {
        "value1": "Testing 1",
        "value2": "Testing 2"
    },
    "windowSecond": {
        "value1": "Testing 1 1",
        "value2": "Testing 1 2"
    }
}

심층 설명

정확히 원래의 코드에 무슨 일에 자세히 설명하기 위해,이 JSON 파서에 무슨 일에 간단하게 살펴 보겠습니다 :

구축 된 JsonNode우린 분석은 다음과 JSON을 나타내는 :

{
    "windowOne": {
        "value1": "Testing 1",
        "value2": "Testing 2"
    },
    "windowSecond": {
        "value1": "Testing 1 1",
        "value2": "Testing 1 2"
    }
}

파서는 토큰 화 이 우리가 함께 작업 할 수 있도록 할 수 있습니다. 의 토큰이 목록으로 이것의 토큰 화 된 상태를 나타냅니다 보자 :

START_OBJECT
FIELD_NAME: "windowOne"
START_OBJECT
FIELD_NAME: "value1"
VALUE: "Testing 1"
FIELD_NAME: "value2"
VALUE: "Testing 2"
END_OBJECT
FIELD_NAME: "windowSecond"
START_OBJECT
FIELD_NAME: "value1"
VALUE: "Testing 1 1"
FIELD_NAME: "value2"
VALUE: "Testing 1 2"
END_OBJECT

잭슨은 그것의 차를 건설하려고,이 토큰을 통해 이동합니다. 그것은 발견 START_OBJECT한 후, FIELD_NAME: "windowOne"그것은해야합니다 알고있는 Window의해 직렬화 복원 CustomDeserialize그것은을 만들고, 그래서 CustomDeserialize그것의 호출 deserialize방법을.

다음 디시리얼라이저 호출 JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);다음 토큰을 예상하는 수하는 START_OBJECT일치 할 때까지 토큰 및 파싱 모든 것을 최대 END_OBJECT로 돌아 토큰 JsonNode.

이것은 반환 JsonNode이 JSON을 나타냅니다를 :

{
    "value1": "window 2 value 1",
    "value2": "window 2 value 2"
}

그리고 파서에 남아있는 토큰은 다음과 같습니다

FIELD_NAME: "windowSecond"
START_OBJECT
FIELD_NAME: "value1"
VALUE: "Testing 1 1"
FIELD_NAME: "value2"
VALUE: "Testing 1 2"
END_OBJECT
END_OBJECT

그런 다음 전화 String field = jsonParser.nextFieldName();로 설명되어있는 :

이 JsonToken.FIELD_NAME 여부 다음 토큰을 페치 (AS nextToken를 호출하는 경우)를 검증 방법; 이 경우, getCurrentName (), 그렇지 않은 경우는 null 같은 반환

즉 그것은 소비 FIELD_NAME: "windowSecond"돌아갑니다 "windowSecond". 그런 다음 다시 부르지 만, 다음 토큰 이후 인 START_OBJECT이 반환 null가.

우리는 지금이

field = "windowSecond"
nextField = null
jsonNode.getNodeType().toString() = "OBJECT"

나머지 토큰 :

FIELD_NAME: "value1"
VALUE: "Testing 1 1"
FIELD_NAME: "value2"
VALUE: "Testing 1 2"
END_OBJECT
END_OBJECT

귀하의 디시리얼라이저는이 점을 회전 Window전달하여 field + nextField( ="windowSecondnull")와 jsonNode.getNodeType().toString( ="OBJECT") 다음 반환, 첫 번째 세트 잭슨 파서 다시 제어 통과 Car.value1창에 귀하의 디시리얼라이저가 반환 한 다음 구문 분석하고 있습니다.

조금 이상한 얻는 곳 여기입니다. 당신의 디시리얼라이저가 반환 된 후, 잭슨은 기대 FIELD_NAME토큰을하고 소비하기 때문에 START_OBJECT토큰이 하나를 가져옵니다. 그러나 도착 FIELD_NAME: "value1"과 이후 Car라는 이름의 속성이없는 value1 그리고 당신은이 필드를 건너 뛰고에에 그것의 가치와 이동 알 수없는 속성을 무시 잭슨를 구성 FIELD_NAME: "value2"하는이 같은 동작이 발생합니다.

이 같은 지금 남아있는 토큰 외모 :

END_OBJECT
END_OBJECT

다음 토큰은 END_OBJECT사용자가 어떤 신호를 Car제대로 잭슨 반환 있도록 직렬화 복원되었습니다.

여기서주의 할 점은 파서는 여전히 하나의 마지막, 토큰이 남아 있다는 것입니다 END_OBJECT 하지만 잭슨은 기본적으로 남아있는 토큰을 무시하기 때문에 오류가 발생하지 않습니다.

당신이 실패보고 싶다면, 줄을 제거 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);:

인식 할 수없는 필드 "VALUE1"(클래스 com.example.demodeserializer.Car), 무시할 수없는 것으로 표시 (2 개 알려진 속성 "windowSecond", "windowOne"])

사용자 정의 디시리얼라이저이 소비하는 토큰

우리가 선을 제거해야 파서 여러 번 호출하는 사용자 정의 디시리얼라이저 작성하려면 JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);대신 토큰에게 자신을 처리합니다.

우리는 이런 것을 할 수 있습니다 :

@Override
public Window deserialize(JsonParser jsonParser, DeserializationContext dc) throws IOException, JsonProcessingException {
    // Assert that the current token is a START_OBJECT token
    if (jsonParser.currentToken() != JsonToken.START_OBJECT) {
        throw dc.wrongTokenException(jsonParser, Window.class, JsonToken.START_OBJECT, "Expected start of Window");
    }

    // Read the next two attributes with value and put them in a map
    // Putting the attributes in a map means we ignore the order of the attributes
    final Map<String, String> attributes = new HashMap<>();
    attributes.put(jsonParser.nextFieldName(), jsonParser.nextTextValue());
    attributes.put(jsonParser.nextFieldName(), jsonParser.nextTextValue());

    // Assert that the next token is an END_OBJECT token
    if (jsonParser.nextToken() != JsonToken.END_OBJECT) {
        throw dc.wrongTokenException(jsonParser, Window.class, JsonToken.END_OBJECT, "Expected end of Window");
    }

    // Create a new window and return it
    return new Window(attributes.get("value1"), attributes.get("value2"));
}

추천

출처http://43.154.161.224:23101/article/api/json?id=209603&siteId=1