杰克逊有很多来自java.util.Date代码的例子,但它们似乎都在利用POJO注释。我有通用的标量映射,我希望将其/序列化为JSON。这是当前的解串器设置;很简单:
public class JSONUtils {
static {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.setDateFormat(df); // this works for outbounds but has no effect on inbounds
mapper.getDeserializationConfig().with(df); // Gave this a shot but still does not sniff strings for a format that we declare should be treated as java.util.Date
}
public static Map<String,Object> parseJSON(InputStream is) {
Map<String,Object> data = null;
try {
data = mapper.readValue(is, Map.class);
} catch(Exception e) {
// ...
}
return data;
}
我知道dateserializer可以将java.util.Date变成ISO 8601-ish字符串。这是困扰我的另一种方式。显然,在没有上下文的JSON文档中,字符串是一个字符串,所以我不知道它是否曾经是一个日期。所以我准备对这种类型进行处理并检查所有被反序列化的字符串,如果它们闻起来像YYYY-MM-DDTHH:MM:SS.sss日期时间,那么我将创建一个java.util.Date而不是仅仅传回一个String。所以给出:
{ "name": "buzz",
"theDate": "2013-09-10T12:00:00.000"
}
将产生
Map<String,Object> m = mapper.readValue(is, Map.class);
Object o1 = m.get("name"); // o1 is instanceof String
Object o2 = m.get("theDate"); // o2 is instanceof Date
但这意味着反序列化器必须返回两种不同的类型,我无法弄清楚如何在杰克逊中做到这一点。有没有人知道一个好的,紧凑的例子,它将嗅探类似日期的字符串并将它们变成日期,将其他人留作字符串?
答案 0 :(得分:4)
如果您有 POJO ,则可以使用序列化器和反序列化器在get和set方法上轻松使用注释。
按照以不同方式序列化和反序列化对象的示例:List<POJO>
到String
,String
到Map
和Map
到List<POJO>
再次。显然,在地图中,Date
值为String
。
此解决方案是线程安全的,因为使用 org.joda.time.format.DateTimeFormat 和 org.joda.time.format.DateTimeFormatter ,您可以在此处找到更多信息这篇文章How to deserialize JS date using Jackson?和此链接http://fahdshariff.blogspot.co.uk/2010/08/dateformat-with-multiple-threads.html
我的POJO:
@JsonAutoDetect
public class QueueTask implements Serializable {
private static final long serialVersionUID = -4411796657106403937L;
public enum ActivitiQueueStatus {
IN_PROGRESS(AsyncProcessingWorkflowContentModel.InProgressTask.TYPE.getLocalName()), //
IN_QUEUE(AsyncProcessingWorkflowContentModel.InQueueTask.TYPE.getLocalName());
private String value;
private ActivitiQueueStatus(final String value) {
this.value = value;
}
public static ActivitiQueueStatus enumOf(final String value) {
for (ActivitiQueueStatus enum_i : values()) {
if (enum_i.value.equals(value))
return enum_i;
}
throw new IllegalArgumentException("value '" + value + "' is not a valid enum");
}
}
private String user;
private Date creationDate;
private int noRowsSelected;
private ActivitiQueueStatus status;
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
@JsonSerialize(using = JsonDateSerializer.class)
public Date getCreationDate() {
return creationDate;
}
@JsonDeserialize(using = JsonDateDeSerializer.class)
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public int getNoRowsSelected() {
return noRowsSelected;
}
public void setNoRowsSelected(int noRowsSelected) {
this.noRowsSelected = noRowsSelected;
}
public ActivitiQueueStatus getStatus() {
return status;
}
public void setStatus(ActivitiQueueStatus status) {
this.status = status;
}
}
My Serializer:
@Component
public class JsonDateDeSerializer extends JsonDeserializer<Date> {
// use joda library for thread safe issue
private static final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("dd/MM/yyyy hh:mm:ss");
@Override
public Date deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
if (jp.getCurrentToken().equals(JsonToken.VALUE_STRING))
return dateFormat.parseDateTime(jp.getText().toString()).toDate();
return null;
}
}
和反序列化器:
@Component
public class JsonDateSerializer extends JsonSerializer<Date> {
// use joda library for thread safe issue
private static final DateTimeFormatter dateFormat = DateTimeFormat.forPattern("dd/MM/yyyy hh:mm:ss");
@Override
public void serialize(final Date date, final JsonGenerator gen, final SerializerProvider provider) throws IOException, JsonProcessingException {
final String formattedDate = dateFormat.print(date.getTime());
gen.writeString(formattedDate);
}
}
我的服务:
public class ServiceMock {
// mock this parameter for usage.
public List<QueueTask> getActiveActivities(QName taskStatus) {
final List<QueueTask> listToReturn = new LinkedList<QueueTask>();
final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
Date d1 = null, d2 = null, d3 = null, d4 = null, d5 = null;
try {
d1 = dateFormat.parse("01/02/2013 12:44:44");
d2 = dateFormat.parse("21/12/2013 16:44:44");
d3 = dateFormat.parse("21/12/2013 16:45:44");
d4 = dateFormat.parse("21/12/2013 16:44:46");
d5 = dateFormat.parse("11/09/2013 16:44:44");
} catch (ParseException e) {
}
QueueTask dataSet = new QueueTask();
dataSet = new QueueTask();
dataSet.setUser("user_b");
dataSet.setStatus(ActivitiQueueStatus.enumOf("placeInQueue"));
dataSet.setNoRowsSelected(500);
dataSet.setCreationDate(d1);
listToReturn.add(dataSet);
dataSet = new QueueTask();
dataSet.setUser("user_d");
dataSet.setStatus(ActivitiQueueStatus.enumOf("placeInQueue"));
dataSet.setNoRowsSelected(300);
dataSet.setCreationDate(d2);
listToReturn.add(dataSet);
dataSet = new QueueTask();
dataSet.setUser("user_a");
dataSet.setStatus(ActivitiQueueStatus.enumOf("inProgress"));
dataSet.setNoRowsSelected(700);
dataSet.setCreationDate(d3);
listToReturn.add(dataSet);
dataSet = new QueueTask();
dataSet.setUser("user_k");
dataSet.setStatus(ActivitiQueueStatus.enumOf("inProgress"));
dataSet.setNoRowsSelected(700);
dataSet.setCreationDate(d4);
listToReturn.add(dataSet);
dataSet = new QueueTask();
dataSet.setUser("user_l");
dataSet.setStatus(ActivitiQueueStatus.enumOf("inProgress"));
dataSet.setNoRowsSelected(700);
dataSet.setCreationDate(d5);
listToReturn.add(dataSet);
return listToReturn;
}
}
主要用途:
public class SerializationServiceTest {
private static final Logger LOGGER = LoggerFactory.getLogger(OUPQueueStatusServiceIT.class);
public void testGetActiveActivitiesSerialization() throws Exception {
LOGGER.info("testGetActiveActivitiesSerialization - start");
ServiceMock mockedService = new ServiceMock();
// AsyncProcessingWorkflowContentModel.InProgressTask.TYPE is an QName, mock this calling
List<QueueTask> tasks = mockedService.getActiveActivities(AsyncProcessingWorkflowContentModel.InProgressTask.TYPE);
assertNotNull(tasks);
assertTrue(tasks.size() == 5);
assertNotNull(tasks.get(0).getUser());
assertNotNull(tasks.get(0).getCreationDate());
assertNotNull(tasks.get(0).getStatus());
assertNotNull(tasks.get(0).getNoRowsSelected());
final ObjectMapper mapper = new ObjectMapper();
final String jsonString = mapper.writeValueAsString(tasks);
assertNotNull(jsonString);
assertTrue(jsonString.contains("creationDate"));
// test serialization from string to Map
final List<Map<String, Object>> listOfMap = mapper.readValue(jsonString, new TypeReference<List<Map<String, Object>>>() {
});
assertNotNull(listOfMap);
final DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
for (Map<String, Object> map_i : listOfMap) {
// check date value
assertTrue(map_i.containsKey("creationDate"));
final Date date = formatter.parse("" + map_i.get("creationDate"));
assertNotNull(date);
assertNotNull(map_i.get("user"));
assertNotNull(map_i.get("status"));
assertNotNull(ActivitiQueueStatus.valueOf("" + map_i.get("status")));
assertNotNull(map_i.get("noRowsSelected"));
}
// test de-serialization
List<QueueTask> deserializedTaskList = mapper.convertValue(listOfMap, new TypeReference<List<QueueTask>>() {
});
assertNotNull(deserializedTaskList);
assertTrue(deserializedTaskList.size() == 5);
for (QueueTask t : deserializedTaskList) {
assertNotNull(t.getUser());
assertNotNull(t.getCreationDate());
assertNotNull(t.getDownloadType());
assertNotNull(t.getStatus());
}
LOGGER.info("testGetActiveActivitiesSerialization - end");
}
public static void main(String[] args) throws Exception {
new SerializationServiceTest().SerializationServiceTest();
}
}
答案 1 :(得分:3)
经过几周的讨论(没有其他评论或答案),我现在相信我所寻求的是不可能在杰克逊。将JSON反序列化为具有日期鸭子类型的Map必须在事后进行。无法插入解析流,嗅探YYYY-MM-DDTHH:MM:SS.SSS
的字符串,并在匹配时替换Date
对象而不是String
。你必须让杰克逊建造Map
,然后在杰克逊之外回到顶部然后走Map
,嗅闻日期。
我将补充说,因为我有一个非常具体的鸭子,所以将String转换为Date的最快实现是一个大约120行的手动卷,验证和设置为日历调整正确的整数mdyhms-ms然后调用getTime()
。 10,000,000次转换需要4240毫秒,或大约2.3米/秒。
在joda-time大厅爆炸之前,是的,我先尝试过:
// This is set up ONCE, outside the timing loop:
DateTimeFormatter format = ISODateTimeFormat.dateHourMinuteSecondMillis();
// These are in the timing loop:
while(loop) {
DateTime time = format.parseDateTime("2013-09-09T14:45:00.123");
Date d = time.toDate();
}
运行约9630毫米,约1.04米/秒;速度的一半。但这仍然比“开箱即用的javax”选项更快:
java.util.Calendar c2 = javax.xml.bind.DatatypeConverter.parseDateTime(s);
Date d = c2.getTime();
这需要30428工厂运行,大约0.33米/秒 - 几乎比手动工具慢7倍。
SimpleDateFormat
不是线程安全的,因此不考虑在转换器实用程序中使用,我无法对调用者做任何假设。
答案 2 :(得分:2)
我最近一直在寻找相关主题的答案,并提出以下解决方案,感谢 Justin Musgrove 和他的文章Custom jackson date deserializer。 基本上,我们的想法是替换Object.class的标准反序列化器,它将指定格式的任何字符串转换为Date对象,否则回退到标准行为。显然,这个操作是以额外处理为代价的,所以你需要为此配置一个专门的ObjectMapper实例,并且只在绝对必要时才使用它,或者无论如何准备做第二遍。
请注意,示例中的日期字符串格式没有时区组件,这可能会导致一些问题,但我会按照请求保留格式。您可以使用您选择的解析器代替 Apache Commons Lang 中的FastDateFormat。我实际上在我的情况下使用Instant。
<强> CustomObjectDeserializer.java 强>
import java.io.IOException;
import org.apache.commons.lang3.time.FastDateFormat;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
public class CustomObjectDeserializer extends UntypedObjectDeserializer {
private static final long serialVersionUID = 1L;
private static final FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
public CustomObjectDeserializer() {
super(null, null);
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
if (p.getCurrentTokenId() == JsonTokenId.ID_STRING) {
try {
String value = p.getText();
// put your own parser here
return format.parse(value);
} catch (Exception e) {
return super.deserialize(p, ctxt);
}
} else {
return super.deserialize(p, ctxt);
}
}
}
<强> JSONUtils.java 强>
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class JSONUtils {
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
SimpleModule module = new SimpleModule("DateConverter");
// register a new deserializer extending and replacing UntypedObjectDeserializer
module.addDeserializer(Object.class, new CustomObjectDeserializer());
mapper.registerModule(module);
}
public static Map<String, Object> parseJSON(InputStream is) {
Map<String, Object> data = null;
try {
data = mapper.readValue(is, Map.class);
} catch (Exception e) {
// ...
e.printStackTrace();
}
return data;
}
public static void main(String[] args) throws Exception {
String input = "{\"name\": \"buzz\", \"theDate\": \"2013-09-10T12:00:00.000\"}";
InputStream is = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8));
Map<String, Object> m = mapper.readValue(is, Map.class);
Object o1 = m.get("name"); // o1 is instanceof String
Object o2 = m.get("theDate"); // o2 is instanceof Date
System.out.println(o1.getClass().getName() + " : " + o1);
System.out.println(o2.getClass().getName() + " : " + o2);
}
}
答案 3 :(得分:-2)
这是一个关于如何使用Jackson序列化从对象反序列化日期的基本示例
公共课JacksonSetup {private static class JacksonSerializer {
private static JacksonSerializer instance;
private JacksonSerializer() {
}
public static JacksonSerializer getInstance() {
if (instance == null)
instance = new JacksonSerializer();
return instance;
}
public <E extends ModelObject> void writeTo(E object, Class<E> type, OutputStream out) throws IOException {
ObjectMapper mapper = getMapper();
mapper.writeValue(out, object);
}
public <E extends ModelObject> void writeTo(E object, Class<E> type, Writer out) throws IOException {
ObjectMapper mapper = getMapper();
mapper.writeValue(out, object);
}
public <E extends ModelObject> E read(String input, Class<E> type) throws IOException {
ObjectMapper mapper = getMapper();
E result = (E) mapper.readValue(input, type);
return result;
}
private ObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
mapper.setAnnotationIntrospector(introspector);
return mapper;
}
}
private static class JaxbDateSerializer extends XmlAdapter<String, Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
@Override
public String marshal(Date date) throws Exception {
return dateFormat.format(date);
}
@Override
public Date unmarshal(String date) throws Exception {
return dateFormat.parse(date);
}
}
private static abstract class ModelObject {
}
private static class Person extends ModelObject {
private String name;
private Date bday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlElement(name = "birth-day")
@XmlJavaTypeAdapter(JaxbDateSerializer.class)
public Date getBday() {
return bday;
}
public void setBday(Date bday) {
this.bday = bday;
}
}
public static void main(String[] args) {
try {
Person person = new Person();
person.setName("Jhon Doe");
person.setBday(new Date());
Writer writer = new StringWriter();
JacksonSerializer.getInstance().writeTo(person, Person.class, writer);
System.out.println(writer.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}