gpt4 book ai didi

datetime - 在将 json 反序列化为对象时,使用 jackson 将 asp.net/MS 专有 json Dateformat 转换为 java8 LocalDateTime

转载 作者:行者123 更新时间:2023-12-05 00:51:20 31 4
gpt4 key购买 nike

我从 Spring Boot 应用程序调用 web 服务,使用 jackson-jsr-310 作为 maven 依赖项,以便能够使用 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);

我的问题在最后一行,代码产生了这个错误:

java.time.format.DateTimeParseException: Text '/Date(591321600000)/' could not be parsed at index 0


response.getBody() 中生成的 JSON看起来像这样:
{  
"d":{
...
"Address":{...},
"FirstName":"asd",
"LastName":"asd",
"BirthDate":"\/Date(591321600000)\/",
}
}

在我的模型类中,我有以下成员:
@JsonProperty("BirthDate")
private LocalDateTime birthDate;

所以,经过一番搜索,我发现这个 /Date(...)/似乎是 Microsoft 专有的 Dateformat,Jackson 默认无法将其反序列化为对象。

一些问题建议创建自定义 SimpleDateFormat并将它应用到我试图做的 opbject 映射器,但后来我想我错过了 mapper.setDateFormat(new SimpleDateFormat("...")); 的正确语法

我试过用例如 mapper.setDateFormat(new SimpleDateFormat("/Date(S)/"));
或者最后甚至 mapper.setDateFormat(new SimpleDateFormat("SSSSSSSSSSSS)"));
但这似乎也不起作用,所以我现在没有想法,希望这里的一些人可以帮助我。

编辑 1:

进一步调查,似乎一种方法是编写自定义 DateDeSerializer为 jackson 。所以我尝试了这个:
@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;
}

}

它实际上几乎返回了我想要的,在模型中使用注释我的字段
@JsonDeserialize(using = JsonDateTimeDeserializer.class)

但不完全是:
此代码返回 LocalDateTime值(value): 1988-09-27T01:00 .
但是在第三方系统中,xmlvalue是 1988-09-27T00:00:00 .

很明显,这里的 ZoneId :
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime), ZoneId.systemDefault());

是问题,除了错误的日期格式。

所以这里有人可以帮我解决如何切换到始终使用零的 time -part 并让我的日期格式正确?会很好!

最佳答案

我假设数字 591321600000是纪元毫秒(从 1970-01-01T00:00:00Z 开始的毫秒数)。

如果是这样,我认为 SimpleDateFormat帮不了你(至少我找不到使用此类从纪元毫秒解析日期的方法)。模式S (根据 javadoc )用于格式化或解析时间的毫秒字段(因此其最大值为 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());
}
}

然后我将此模块添加到我的映射器中并且它起作用了:
// 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

现在对反序列化器方法进行一些评论。

首先我转换了millis 591321600000Instant (代表 UTC 时刻的类)。 591321600000以毫秒为单位等于 1988-09-27T00:00:00Z .

但那是 UTC 日期/时间。获取 本地 日期和时间,你必须知道你在哪个时区,因为在每个时区都是不同的日期和时间(世界上每个人都在同一时刻,但他们的本地日期/时间可能不同,具体取决于他们在哪里) .

在我的示例中,我只使用了 ZoneId.systemDefault() ,它获取我系统的默认时区。但是,如果您不想依赖默认值并想使用特定时区,请使用 ZoneId.of("timezone name")方法(您可以使用 ZoneId.getAvailableZoneIds() 获取所有可用时区名称的列表 - 此方法返回 ZoneId.of() 方法接受的所有有效名称)。

因为我的默认时区是 America/Sao_Paulo ,此代码设置 birthDate1988-09-26T21:00 .

如果您不想转换到特定时区,可以使用 ZoneOffset.UTC .因此,在解串器方法中,最后一行将是:
   return instant.atZone(ZoneOffset.UTC).toLocalDateTime();

现在本地日期将是 1988-09-27T00:00 - 因为我们使用的是 UTC 偏移量,所以没有时区转换,本地日期/时间也没有改变。

PS:如果您需要转换 birthDate回到 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)/

请注意,要转换 LocalDateTimeInstant ,你必须知道你在哪个时区。在这种情况下,我建议使用相同的时区进行序列化和反序列化(在您的情况下,您可以使用 ZoneOffset.UTC 而不是 ZoneId.systemDefault()

关于datetime - 在将 json 反序列化为对象时,使用 jackson 将 asp.net/MS 专有 json Dateformat 转换为 java8 LocalDateTime,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44432789/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com