我正在使用杰克逊(Jackson)从API读取JSON数据,大多数时候,我会得到一系列在其实现中都相当标准的对象。唯一的问题是,日期有时会采用“ yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”格式,有时会采用“ yyyy-MM-dd”格式,因此JSON看起来像这个:
[
{
'A':'Foo'
'B':'2016-11-03T12:35:23.032Z'
'C':'7'
},
{
'A':'Bar'
'B':'2016-11-06'
'C':'4'
},
{
'A':'Bla'
'B':'2016-11-07T14:42:18.832Z'
'C':'23'
},
{
'A':'Blo'
'B':'2016-11-07T15:12:23.439Z'
'C':'9'
}
]
每次到达第二个日期,我都会收到一个解析器错误,因为它的格式不同。我尝试编写一个类,如果第一个失败,它将使用第二个DateFormat,但是现在我只得到了NullPointerException。
public class BackupDateFormat extends DateFormat {
private final LinkedList<DateFormat> formats;
public BackupDateFormat(DateFormat... dfs) {
formats = new LinkedList<>(Arrays.asList(dfs));
}
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
return formats.getFirst().format(date, toAppendTo, fieldPosition);
}
@Override
public Date parse(String source, ParsePosition pos) {
return formats.getFirst().parse(source, pos);
}
@Override
public Date parse(String source) throws ParseException {
ParseException exception = null;
for (DateFormat df : formats) {
try {
return df.parse(source);
}
catch (ParseException pe) {
exception = pe;
}
}
throw exception;
}
}
他是我得到的错误:
com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.company.api.API$Result["result"]->java.util.ArrayList[0]->com.company.models.othercompany.Record["dateTime"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1597)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:278)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:294)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:266)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:485)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:108)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3836)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2860)
at com.mentoredata.api.API.get(API.java:56)
这是第56行的代码:
return mapper.<List<T>>readValue(new URL(url), mapper.getTypeFactory().constructCollectionType(List.class, type));
有谁知道我该如何修正我的当前代码以不获取空指针或在Jackson中使用两种dateformat?
最佳答案
我无法确切说明您的问题是什么,但是我能够使您的代码正常工作。首先,我创建了一个POJO,表示您要反序列化的对象。我假设您已经有一个同等学历,但这是我的:
class Obj {
String A;
Date B;
Integer C;
/* with getters/setters */
}
然后,我使用对象映射器创建了一个自定义解串器。该类并不是很难实现。我在反序列化器内部使用了BackupDateFormat:
class ObjDeserializer extends JsonDeserializer<Obj> {
final BackupDateFormat backupDateFormat;
public ObjDeserializer() {
backupDateFormat = new BackupDateFormat(
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
new SimpleDateFormat("yyyy-MM-dd"));
}
@Override
public Obj deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// create a POJO and populate with the fields from the JSON Object
Obj obj = new Obj();
JsonNode root = p.readValueAsTree();
obj.setA(root.get("A").asText(""));
obj.setC(root.get("C").asInt(0));
try {
obj.setB(backupDateFormat.parse(root.get("B").asText()));
} catch (ParseException e) {
throw new IOException("Could not parse date as expected.");
}
return obj;
}
}
之后,向ObjectMapper注册序列化器:
SimpleModule dateDeserializerModule = new SimpleModule();
// associate the custom deserializer with your POJO
dateDeserializerModule.addDeserializer(Obj.class, new ObjDeserializer());
mapper.registerModule(dateDeserializerModule);
最后,您可以看到从该代码段及其输出中正确地解析了日期:
List<Obj> result = mapper.readValue(input.getBytes(), mapper.getTypeFactory().constructCollectionType(List.class, Obj.class));
result.forEach(x->System.out.println(x.getB().toString()));
Thu Nov 03 12:35:23 EDT 2016Sun Nov 06 00:00:00 EDT 2016Mon Nov 07 14:42:18 EST 2016Mon Nov 07 15:12:23 EST 2016
如果您发现Jackson内置的功能可以做到这一点,我建议您使用它,但是有时您最终需要此方法提供的额外级别的自定义。希望能帮助到你。
也查看以下问题。相似,但不处理多种格式。接受的答案也将使用自定义反序列化器。
参考文献:
Similar SO Question
Getting started with deserializers