使用jackson将asp.net/MS专有json Dateformat转换为java8 LocalDateTime,同时将json反序列化为对象
我从Spring Boot应用程序调用一个Web服务,使用jackson-jsr-310作为maven依赖项,以便能够利用使用jackson将asp.net/MS专有json Dateformat转换为java8 LocalDateTime,同时将json反序列化为对象,datetime,spring-boot,java-8,jackson,deserialization,Datetime,Spring Boot,Java 8,Jackson,Deserialization,我从Spring Boot应用程序调用一个Web服务,使用jackson-jsr-310作为maven依赖项,以便能够利用LocalDateTime: RestTemplate restTemplate = new RestTemplate(); HttpHeaders httpHeaders = this.createHeaders(); ResponseEntity<String> response; response = restTemplate.exchange(uri,H
LocalDateTime
:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders httpHeaders = this.createHeaders();
ResponseEntity<String> response;
response = restTemplate.exchange(uri,HttpMethod.GET,new HttpEntity<Object>(httpHeaders),String.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
BusinessPartner test = mapper.readValue(response.getBody(), BusinessPartner.class);
在我的模型课上,我有以下成员:
@JsonProperty("BirthDate")
private LocalDateTime birthDate;
所以,在这里搜索了一段时间后,我发现这个/Date(…)/
似乎是Microsoft专有的Dateformat,Jackson无法按默认值反序列化为对象
有些问题建议创建一个自定义的SimpleDateFormat
并将其应用于opbject映射器,我曾尝试过这样做,但我认为我错过了mapper.setDateFormat(新的SimpleDateFormat(“…”)的正确语法代码>
我试过使用例如mapper.setDateFormat(新的SimpleDateFormat(“/Date/”)代码>
或者在末尾偶数mapper.setDateFormat(新的SimpleDataFormat(“SSSS”)代码>
但这似乎也不起作用,所以我现在没有主意了,希望这里的一些人能帮助我
编辑1:
进一步研究,一种方法似乎是为jackson编写一个定制的DateDeSerializer
。所以我试了一下:
@Component
public class JsonDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
private DateTimeFormatter formatter;
private JsonDateTimeDeserializer() {
this(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
public JsonDateTimeDeserializer(DateTimeFormatter formatter) {
this.formatter = formatter;
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException
{
if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
String unixEpochString = parser.getText().trim();
unixEpochString = unixEpochString.replaceAll("[^\\d.]", "");
long unixTime = Long.valueOf(unixEpochString);
if (unixEpochString.length() == 0) {
return null;
}
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime), ZoneId.systemDefault());
localDateTime.format(formatter);
return localDateTime;
}
return null;
}
但不完全是:
此代码返回一个LocalDateTime
值:1988-09-27T01:00
。
但是在第三方系统中,xmlvalue是1988-09-27T00:00:00
显而易见,这里的区域ID:
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime), ZoneId.systemDefault());
除了错误的日期格式之外,还有一个问题
因此,这里有人能帮助我如何切换到总是使用零作为时间
部分,并使我的日期格式正确吗?那太好了 我假设数字591321600000
是纪元毫秒(从1970-01-01T00:00:00Z开始的毫秒数)
如果是这样的话,我认为SimpleDateFormat
不能帮助您(至少我找不到一种方法来使用这个类解析来自epoch mili的日期)。模式S
(根据)用于格式化或解析时间的毫秒字段(因此其最大值为999),不适用于您的情况
我能让它工作的唯一方法是创建一个自定义反序列化程序
首先,我创建了这个类:
public class SimpleDateTest {
@JsonProperty("BirthDate")
private LocalDateTime birthDate;
// getter and setter
}
然后,我创建了自定义反序列化程序并将其添加到自定义模块:
// I'll explain all the details below
public class CustomDateDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String s = p.getText(); // s is "/Date(591321600000)/"
// assuming the format is always /Date(number)/
long millis = Long.parseLong(s.replaceAll("\\/Date\\((\\d+)\\)\\/", "$1"));
Instant instant = Instant.ofEpochMilli(millis); // 1988-09-27T00:00:00Z
// instant is in UTC (no timezone assigned to it)
// to get the local datetime, you must provide a timezone
// I'm just using system's default, but you must use whatever timezone your system uses
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
public class CustomDateModule extends SimpleModule {
public CustomDateModule() {
addDeserializer(LocalDateTime.class, new CustomDateDeserializer());
}
}
下面是关于反序列化器方法的一些注释
首先,我将millis591321600000
转换为一个瞬间
(一个表示UTC瞬间的类)<代码>591321600000
单位为毫秒等于1988-09-27T00:00:00Z
但这是UTC的日期/时间。要获取本地的日期和时间,您必须知道您所在的时区,因为在每个时区都是不同的日期和时间(世界上每个人都在同一时刻,但他们的本地日期/时间可能不同,具体取决于他们在哪里)
在我的示例中,我刚刚使用了ZoneId.systemDefault()
,它获取系统的默认时区。但是,如果您不想依赖默认值并希望使用特定时区,请使用ZoneId.of(“时区名称”)
方法(您可以使用ZoneId.getAvailableZoneIds()
-此方法返回ZoneId.of()
方法接受的所有有效名称)
由于我的默认时区是美国/圣保罗
,此代码将出生日期设置为1988-09-26T21:00
如果不想转换为特定时区,可以使用ZoneOffset.UTC
。因此,在反序列化器方法中,最后一行是:
return instant.atZone(ZoneOffset.UTC).toLocalDateTime();
现在本地日期将是1988-09-27T00:00
——因为我们使用UTC偏移,所以没有时区转换,本地日期/时间也没有更改
PS:如果您需要将生日
转换回MS的自定义格式,您可以编写自定义序列化程序并添加到自定义模块中。要将LocalDateTime
转换为该格式,可以执行以下操作:
LocalDateTime birthDate = value.getBirthDate();
// you must know in what zone you are to convert it to epoch milli (using default as an example)
Instant instant = birthDate.atZone(ZoneId.systemDefault()).toInstant();
String msFormat = "/Date(" + instant.toEpochMilli() + ")/";
System.out.println(msFormat); // /Date(591321600000)/
请注意,要将LocalDateTime
转换为Instant
,您必须知道您所在的时区。在这种情况下,我建议使用相同的时区进行序列化和反序列化(在您的情况下,您可以使用ZoneOffset.UTC
而不是ZoneId.systemDefault()
以下是我编写的一些Groovy代码,它也处理时区偏移:
类JSONDOTTNetLocalDateTimeDeserializer扩展JsonDeserializer{
@凌驾
LocalDateTime反序列化(JsonParser解析器,反序列化上下文ctxt){
convertDotNetDateToJava(parser.text.trim())
}
/**
*当给定.Net日期字符串时,返回Java LocalDateTime
*/日期(1535491858840-0500)/
*/
静态LocalDateTime转换器DotNetDateToJava(字符串dotNetDate){
//去掉前缀和后缀仅为1535491858840-0500
字符串epochAndOffset=dotNetDate[6..-3]
// 1535491858840
字符串历元=偏移量[0..-6]
//-0500注意,保持负/正指示灯
字符串偏移量=偏移量[-5..-1]
ZoneId ZoneId=ZoneId.of(“UTC${offset}”)
LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch.toLong()),zoneId)
}
}
591321600000
是纪元毫秒(从1970-01-01T00:00:00Z算起的毫秒)?@Hugo是的,请查看我的编辑以了解更多信息。System.out.println(ZoneId.systemDefault())的输出是什么?
是欧洲/柏林(这里下雨)偏移量为+01:00,这就是为什么我说问题很明显。不考虑来自圣保罗的人;)事实上,这与我得到的非常接近:)我接受你的答案,因为这实际上解决了95%。如果你能帮我解决编辑中最后的问题,那就太好了!发生时移是因为您将UTC值(瞬间)转换为yo
// using reduced JSON with only the relevant field
String json = "{ \"BirthDate\": \"\\/Date(591321600000)\\/\" }";
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
// add my custom module
mapper.registerModule(new CustomDateModule());
SimpleDateTest value = mapper.readValue(json, SimpleDateTest.class);
System.out.println(value.getBirthDate()); // 1988-09-26T21:00
return instant.atZone(ZoneOffset.UTC).toLocalDateTime();
LocalDateTime birthDate = value.getBirthDate();
// you must know in what zone you are to convert it to epoch milli (using default as an example)
Instant instant = birthDate.atZone(ZoneId.systemDefault()).toInstant();
String msFormat = "/Date(" + instant.toEpochMilli() + ")/";
System.out.println(msFormat); // /Date(591321600000)/
class JsonDotNetLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
LocalDateTime deserialize(JsonParser parser, DeserializationContext ctxt) {
convertDotNetDateToJava(parser.text.trim())
}
/**
* Returns a Java LocalDateTime when given a .Net Date String
* /Date(1535491858840-0500)/
*/
static LocalDateTime convertDotNetDateToJava(String dotNetDate) {
// Strip the prefix and suffix to just 1535491858840-0500
String epochAndOffset = dotNetDate[6..-3]
// 1535491858840
String epoch = epochAndOffset[0..-6]
// -0500 Note, keep the negative/positive indicator
String offset = epochAndOffset[-5..-1]
ZoneId zoneId = ZoneId.of("UTC${offset}")
LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch.toLong()), zoneId)
}
}