3

I have JSON looking like this, let say Question

{
    "type": "String",
    "value": "{\"text\":\"Question\",\"type\":\"predefined\",\"parameter\":\"param1\",\"answers\":[\"Answer1\",\"Answer2\",\"Answer3\",\"Answear4\"]}",
    "valueInfo": {}
}

and i want to parse it with Jackson to Question objecti with object Value inside contains details about question (like text, type, and a list of answers)

i try to create classes Question and Value

public class AbasQuestion {
        @JsonProperty("type")
        String type;
        @JsonProperty("value")
        Value value;
        JsonNode valueInfo; 
} 
public class Value {

    String text;
    String type;
    String parameter;
    List<String> answers;
}

and parse string to them with

Question question = objectMapper.readValue(jsonQuestion, Question.class);

but stil i get error

Can not instantiate value of type [simple type, class Value] from String value; no single-String constructor/factory method (through reference chain: Question["value"])

I understan that Value is String and i have to convert it to Value object but how and wher ? inside Value constructor or inside Question setter ? to Jackson could do map it.

1
  • I have not worked enough with Jackson because I did not find it intuitive enough for me, but the JSON Path library did wonders for me. Moreover you can try whatever query you want to make in online tools before you insert them into your code. Commented Dec 17, 2021 at 10:28

1 Answer 1

2

You need a custom string-to-value converter. Here's an example using Spring boot as the framework.

@JsonDeserialize(converter = StringToValueConverter.class)
Value value;

If you have a container of Value e.g. List<Value> then it should be contentConverter instead of converter.

Then your converter looks like this.

@Component
public class StringToValueConverter extends StdConverter<String, Value> {

  final ObjectMapper objectMapper;

  public StringToValueConverter(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public Value convert(String value) {
    try {
      return this.objectMapper.readValue(value, Value.class);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
}

You can replace the simple RuntimeException wrapper with something more informative if you need to.

The members of your Value class either need to be public or better provide getter/setters that Jackson can use otherwise all the members will appear to be null after deserialization.

Sign up to request clarification or add additional context in comments.

1 Comment

Just wondering: how to make it generic supporting parameterized types so that the deserializer could be reused for fields of other types?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.