2015-09-03 10 views
0

Я пытаюсь создать собственный класс десериализатора для моего класса значений. Но ObjectMapper .readValue вызывается несколько раз, пока не вызывается StackOverflowException. Ниже приведен пример только:ObjectMapper readValue вызывается несколько раз, пока не вызывается StackOverflowException.

@JsonDeserialize(using = UserDeserializer.class) 
@Getter 
public class User { 
    private String name; 
} 

public class UserDeserializer extends StdDeserializer<User> { 
    public UserDeserializer() { 
     super(User.class); 
    } 

    @Override 
    public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
     return ctxt.readValue(p, User.class); 
    } 
} 

public class UserDeserializerTest { 
    @Test 
    public void whenUserIsDeserializedFromJsonThenNameShouldEqualBob() { 
     String = "{ \"name\" : \"bob\" }"; 
     ObjectMapper mapper = new ObjectMapper(); 
     User user = mapper.readValue(json, User.class); 
     assertThat(user.getName).isEqualTo("bob"); 
    } 
} 

зависимостями Maven:

<dependencies> 
    <dependency> 
     <groupId>com.fasterxml.jackson.core</groupId> 
     <artifactId>jackson-databind</artifactId> 
     <version>2.6.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.assertj</groupId> 
     <artifactId>assertj-core</artifactId> 
     <version>1.7.1</version> 
    </dependency> 
    <dependency> 
     <groupId>org.projectlombok</groupId> 
     <artifactId>lombok</artifactId> 
     <version>1.16.2</version> 
    </dependency> 
    <dependency> 
     <groupId>junit</groupId> 
     <artifactId>junit</artifactId> 
     <version>4.12</version> 
     <scope>test</scope> 
    </dependency> 
</dependencies> 

Следующая StackOverflowException брошено:

Exception in thread "main" java.lang.StackOverflowError 
... 

// The following lines are repeated. 

    at com.fasterxml.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:754) 
    at com.fasterxml.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:746) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:25) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:15) 

Process finished with exit code 1 

Я попытался регистрации десериализатор через модуль и удаления @ JsonAnnotation и по-прежнему вызывает то же исключение:

SimpleModule module = new SimpleModule("User Simple Module"); 
module.addDeserializer(User.class, new UserDeserializer()); 
ObjectMapper mapper = new ObjectMapper(); 
mapper.addModule(module); 
mapper.readValue(json, User.class); 

Затем я попытался создать новый ObjectMapper в своем UserDeserializer и удалить @JsonDeserialize из моего класса User и моих тестовых проходов.

@Override 
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
    return new ObjectMapper().readValue(p, User.class); 
} 

Хотя делать вышеуказанные работы, я хочу, чтобы зарегистрировать десериализатор на каждый класс основе с использованием @JsonDeserialize аннотации, а не регистрирующей глобальным пользовательский объектом картографом. Есть причины, по которым я так хочу.

Я попробовал другие комбинации кода, например:

// Add the 'as' keyword to the annotation 
@JsonDeserialise(using = UserDeserialize.class, as = User.class) 

// Locally assigning a mapper from the JsonParser inside the UserDeserializer class 
ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
mapper.readValue(p, User.class); 

// Constructing a new JSON Parser inside the deserializer 
ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
JsonFactory factory = mapper.getFactory(); 
JsonParser parser = factory.createParser(p.getText()); 
return (User) parser.readValueAs(handledType()); 

// Reading the Json into a Tree 
ObjectMapper mapper = (ObjectMapper) p.getCodec(); 
ObjectNode root = (ObjectNode) p.readValueAsTree(); 
return mapper.readValue(root.traverse(), User.class); 

Но чтение JSON в дерево бросает:

Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalStateException: No ObjectCodec defined for parser, needed for deserialization 
    at example.User.main(User.java:46) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) 
Caused by: java.lang.IllegalStateException: No ObjectCodec defined for parser, needed for deserialization 
    at com.fasterxml.jackson.core.JsonParser._codec(JsonParser.java:1565) 
    at com.fasterxml.jackson.core.JsonParser.readValueAsTree(JsonParser.java:1559) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:37) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:17) 
    at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3674) 
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1996) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:38) 
    at example.UserDeserializer.deserialize(UserDeserializer.java:17) 
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3702) 
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2714) 
    at example.User.main(User.java:36) 
    ... 5 more 

Я также попытался реализации ContextualDeserializer внутри моего UserDeserializer, но все же получить тот же StackOverflowException, как и прежде:

private UserDeserializer(Class<?> vc) { 
    super(vc); 
} 

public JsonDeserializer<User> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { 
    return new UserDeserializer(ctxt.getContextualType().getRawClass()); 
} 

@Override 
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
    return (User) new ObjectMapper().readValue(p, handledType()); 
} 

Я надеюсь, что это просто плохой случай ошибки PEBKAC, и решение легко доступно с вашей помощью/руководством.

Сердечные приветы

+0

Просто иметь в виду, что неповторяющихся вершина StackTrace не имеет никакого отношения к диагностическому. –

+0

Спасибо Марко. Я удалил ненужную часть stacktrace и оставил самый важный бит. – Gary

+0

Вы проверили, что происходит в 'DeserializationContext.java: 754'? Вы должны иметь возможность отслеживать звонки и видеть, что заставляет его перезванивать в себя. –

ответ

1

Я согласен с KDM, попробуйте этот десериализатор:

public class UserDeserializer extends JsonDeserializer<User> { 

    @Override 
    public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
     JsonNode node = p.readValueAsTree(); 
     User user = new User(); 
     user.setName(node.get("name").asText()); 
     return user; 
    } 
} 
+0

@Marko - в строке 754 он присваивает [email protected] (_valueClass = "class example.User", а затем вызывает метод десериализации на десериализаторе. Затем вызывает ctxt.readValue (p, User.class) вернуться к строке 754, очевидно заканчивая порочным циклом. – Gary

+0

@KDM - Вы правы в бесконечном цикле, но я не хочу создавать конкретный класс внутри десериализатора. В конечном итоге я пытаюсь найти общий десериализатор, который любой объект может быть реконструирован из JSON.Я надеялся, что волшебство ObjectMapper поможет мне здесь, а не пытается разрешить весь объект через Reflection. У меня есть пользовательские аннотации, которые я буду аннотировать мои классы значений, и десериализатор должен обрабатывать их по-разному на этапе строительства. – Gary

+0

@ KDM. Во время первого цикла построения объекта мои аннотации будут проигнорированы через DeserializationProblemHandler, чтобы сказать, что они будут обработаны позже. Как только первый этап десериализации произошел, используйте Reflection, чтобы помочь обнаружить аннотации и построить специализированные классы для добавления в класс значений, который реализует интерфейс контракта. – Gary

Смежные вопросы