1

I'm new to Spring-boot and am trying to deserialize json array into java String using Jackson in a Spring-boot Application. Something like

{"history": ["historyA", "historyB"]} (JSON Request Body) -> String history;

However, the following error message got logged.

Cannot deserialize instance of `java.lang.String` out of START_ARRAY token

My Controller is similar to

@RestController
public class PatientController {
    @PostMapping
    public void create(@RequestBody @Valid Patient patient) {
        mapper.create(patient);
    }
}

My POJO is similar to:

@Data
@NoArgsConstructor
public class Patient {
    @JsonDeserialize(contentUsing = PatientHistoryDeserializer.class, contentAs = List.class)
    private String history;

My Json Deserializer is similar to:

public class PatientHistoryDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        List<String> histories = new LinkedList<>();
        if (p.getCurrentToken() == JsonToken.START_ARRAY) {
            while (p.getCurrentToken() != JsonToken.END_ARRAY) {
                String history = p.getValueAsString();
                if(history.contains("#"))
                    throw new ClientError(HttpServletResponse.SC_BAD_REQUEST, "invalid...");
                histories.add(history);
            }
        }
        return String.join("#", histories);
    }
}

Is my goal achievable ? Or any suggestions on how to convert as I wanted ?

2
  • You can deserialize as List normally and in getter join all string and return. Commented May 21, 2020 at 3:16
  • I have my own JsonSerializer for that. BTW, Is it possible to join them in setter in the first place ? Commented May 21, 2020 at 8:33

1 Answer 1

3

This can be done like this

public class PatientHistoryDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
       if (jsonParser.currentToken() == JsonToken.START_ARRAY) {
           List<String> histories = new ArrayList<>();
           jsonParser.nextToken();

           while (jsonParser.hasCurrentToken() && jsonParser.currentToken() != JsonToken.END_ARRAY) {
              histories.add(jsonParser.getValueAsString());
              jsonParser.nextToken();
           }
          return String.join("#", histories);
       }
       return null;
    }
}

And usage would like

@JsonDeserialize(using = PatientHistoryDeserializer.class)
String histories;

The purpose of contentUsing and contentAs are a bit different than the use case here. let's take the following example.

class Histories {
    Map<String, String> content;
}

and JSON is something like this

{"content": { "key" : ["A","B"]}}

and you want to deserialize this into a map having (key = "A#B")

there are two ways to do it, write custom deserializer or use contentUsing attribute to specify how your values should be deserialized

@JsonDeserialize(contentUsing = PatientHistoryDeserializer.class)
Map<String, String> content;

Similarly, you can use other annotation attributes like keyUsing for keys for maps.

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

1 Comment

Your code works perfectly! Thank you for explaining that to me. You really save my day!

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.