转载请注明出处: https://www.codelast.com/
如果一个JSON字符串里有一个字段,它的值又是一个完整的JSON字符串,那么这时候,它可能会含有转义字符。举个例子,我们有一个文件 codelast.json,它的内容为:
{"aaa":"{/"ccc/":/"ccc/",/"ddd/":/"ddd/"}","bbb":{"ccc":"ccc","ddd":"ddd"}}
现在要用Jackson解析它为一个Java对象,怎么做?
我们先把这个JSON格式化,便于观察它的结构:
{ "aaa": "{/"ccc/":/"ccc/",/"ddd/":/"ddd/"}", "bbb": { "ccc": "ccc", "ddd": "ddd" } }
所以按照Jackson的套路,我们似乎很容易就可以写出对应的JSON解析代码,先定义几个POJO:
A.java 的主要代码:
public class A { private AAA aaa; private BBB bbb; public AAA getAaa() { return aaa; } public void setAaa(AAA aaa) { this.aaa = aaa; } public BBB getBbb() { return bbb; } public void setBbb(BBB bbb) { this.bbb = bbb; } }
文章来源: https://www.codelast.com/
AAA.java 的主要代码:
public class AAA { private String ccc; private String ddd; public String getCcc() { return ccc; } public void setCcc(String ccc) { this.ccc = ccc; } public String getDdd() { return ddd; } public void setDdd(String ddd) { this.ddd = ddd; } }
文章来源: https://www.codelast.com/
BBB.java 的主要代码:
public class BBB { private String ccc; private String ddd; public String getCcc() { return ccc; } public void setCcc(String ccc) { this.ccc = ccc; } public String getDdd() { return ddd; } public void setDdd(String ddd) { this.ddd = ddd; } }
文章来源: https://www.codelast.com/
然后就是解析的逻辑:
String json = FileUtils.readFileToString(new File("/home/codelast/codelast.json")); ObjectMapper mapper = new ObjectMapper(); A data = mapper.readValue(json, A.class);
我们的目的是把codelast.json这个文件里保存的JSON字符串解析成类A的一个对象data。
这不是很简单嘛?要不运行一下试试看?
马上报错:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.codelast.AAA` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"ccc":"ccc","ddd":"ddd"}')
at [Source: (String)"{"aaa":"{/"ccc/":/"ccc/",/"ddd/":/"ddd/"}","bbb":{"ccc":"ccc","ddd":"ddd"}}
"; line: 1, column: 8] (through reference chain: com.codelast.A["aaa"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1342)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1031)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1366)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
文章来源: https://www.codelast.com/
这是什么情况?事实上,这就是由于JSON的“aaa”这个字段里面包含转义字符造成的,如果你Google一下,可能会找到 这个 知乎帖子,它告诉我们,要为Jackson的 ObjectMapper 设置一个属性为true: Feature.ALLOW_UNQUOTED_CONTROL_CHARS
所以我们可以试试:
String json = FileUtils.readFileToString(new File("/home/codelast/codelast.json")); ObjectMapper mapper = new ObjectMapper(); mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); A data = mapper.readValue(json, A.class);
再执行代码,会发现报一样的错。
所以上面的解决方案是 无效 的。
文章来源: https://www.codelast.com/
正确解决这个问题的姿势,是参考 Stack Overflow的 这个 帖子。里面说到,应该创建一个 JsonDeserializer 来搞定这个问题。下面,我就把正确的代码依次贴上来:
A.java 的主要代码(和之前不一样,这里使用了一个自定义的
AAADeserializer
):
public class A { @JsonDeserialize(using = AAADeserializer.class) private AAA aaa; private BBB bbb; public AAA getAaa() { return aaa; } public void setAaa(AAA aaa) { this.aaa = aaa; } public BBB getBbb() { return bbb; } public void setBbb(BBB bbb) { this.bbb = bbb; } }
文章来源: https://www.codelast.com/
AAA.java 的主要代码(和之前一样没变化):
public class AAA { private String ccc; private String ddd; public String getCcc() { return ccc; } public void setCcc(String ccc) { this.ccc = ccc; } public String getDdd() { return ddd; } public void setDdd(String ddd) { this.ddd = ddd; } }
文章来源: https://www.codelast.com/
BBB.java的主要代码(和之前一样没变化):
public class BBB { private String ccc; private String ddd; public String getCcc() { return ccc; } public void setCcc(String ccc) { this.ccc = ccc; } public String getDdd() { return ddd; } public void setDdd(String ddd) { this.ddd = ddd; } }
文章来源: https://www.codelast.com/
下面就是关键的 AAADeserializer.java 的主要代码:
public class AAADeserializer extends JsonDeserializer<AAA> { private static final ObjectMapper mapper = new ObjectMapper(); @Override public AAA deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { return mapper.readValue(jsonParser.getText(), AAA.class); } }
文章来源: https://www.codelast.com/
然后就可以解析JSON了:
String json = FileUtils.readFileToString(new File("/home/codelast/codelast.json")); ObjectMapper mapper = new ObjectMapper(); A data = mapper.readValue(json, A.class);
你这时候再运行一下这段代码,就会发现不再报错,JSON已经成功解析到对象 data 中,问题解决!
[原创] 如何用Jackson解析含有转义字符的字符串到Java对象?