Spring Boot Jackson反序列化String[]报START_OBJECT错误解决
报错核心是反序列化类型不匹配:你在POJO里把payLoad声明为String[](字符串数组),但实际传入的JSON中payLoad存储的是结构化JSON对象数组。Jackson反序列化时,读到数组内的对象起始标识{(即异常中提到的JsonToken.START_OBJECT token),无法直接将嵌套的JSON对象转为普通字符串,因此抛出MismatchedInputException。
你之前添加的@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)注解,作用仅为兼容「接口传单个值而非数组时自动包装为数组」的场景,完全无法解决JSON对象转字符串的问题,在此处添加无效。
以下两种方案都可以实现「保留原有JSON格式、将payLoad转为字符串集合/数组」的需求,可根据场景选择:
方案1:自定义反序列化器(推荐)
该方案解析完成后字段直接为目标类型,无需二次转换,反序列化器可全局复用。
- 编写自定义反序列化器,遍历数组时将每个JSON节点直接转为标准JSON字符串:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class JsonArrayToStringListDeserializer extends JsonDeserializer<List<String>> { @Override public List<String> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { List<String> result = new ArrayList<>(); if (p.currentToken() != JsonToken.START_ARRAY) { ctxt.reportInputMismatch(List.class, "payLoad字段预期为数组类型"); return result; } ObjectMapper mapper = (ObjectMapper) p.getCodec(); while (p.nextToken() != JsonToken.END_ARRAY) { JsonNode node = mapper.readTree(p); // 直接将节点转为JSON字符串,完全保留原有结构和格式 result.add(node.toString()); } return result; } } - 修改Response类,为payLoad字段指定自定义反序列化器,移除无效注解:
如果一定要用import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.List; public class Response { @JsonProperty("count") private int totalCount; @JsonProperty("respCode") private int responseCode; @JsonProperty("payLoad") @JsonDeserialize(using = JsonArrayToStringListDeserializer.class) private List<String> transactionsList; // getter/setter public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getResponseCode() { return responseCode; } public void setResponseCode(int responseCode) { this.responseCode = responseCode; } public List<String> getTransactionsList() { return transactionsList; } public void setTransactionsList(List<String> transactionsList) { this.transactionsList = transactionsList; } }String[]类型,只需修改反序列化器返回值为String[],最后执行return result.toArray(new String[0]),同步修改Response类对应字段类型即可。
方案2:先接收JsonNode再手动转换(轻量场景适用)
该方案无需额外编写反序列化类,适合临时使用的简单场景:
- 修改Response类,将payLoad字段声明为Jackson内置的JsonNode类型,手动添加转换方法:
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import java.util.ArrayList; import java.util.List; public class Response { @JsonProperty("count") private int totalCount; @JsonProperty("respCode") private int responseCode; @JsonProperty("payLoad") private JsonNode payLoadNode; // 手动转换为字符串列表 public List<String> getTransactionsList() { List<String> result = new ArrayList<>(); if (payLoadNode != null && payLoadNode.isArray()) { for (JsonNode node : payLoadNode) { result.add(node.toString()); } } return result; } // 其他字段getter/setter省略 }
注意:不要直接将payLoad声明为
List<Map<String,Object>>类型接收,这种方式会把JSON对象转为Map结构,后续再序列化为JSON字符串时可能出现字段顺序变化、null值处理规则不一致的问题,无法保证和原有JSON格式完全一致。
两种方案解析完成后,transactionsList中的每个元素都是payLoad数组内单个对象的完整JSON字符串,比如第一个元素值为{"id":1,"userName":null,"arName":"A1","areas":[]},完全保留原有结构无丢失。
内容的提问来源于stack exchange,提问作者Vikrant

