F. Henry
F. Henry

Reputation: 104

Trying to deserialize json array into java String using Jackson in Spring-boot

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 ?

Upvotes: 1

Views: 4002

Answers (1)

lucid
lucid

Reputation: 2890

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.

Upvotes: 2

Related Questions