加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

使用jackson将asp.net / MS专有的json Dateformat转换为java8 Lo

发布时间:2020-12-16 06:40:27 所属栏目:asp.Net 来源:网络整理
导读:我从 Spring Boot App调用web服务,使用jackson-jsr-310作为maven依赖,以便能够使用LocalDateTime: RestTemplate restTemplate = new RestTemplate();HttpHeaders httpHeaders = this.createHeaders();ResponseEntityString response;response = restTemplat
我从 Spring Boot App调用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(…)/似乎是一个微软专有的Dateformat,杰克逊不能默认反序列化为一个对象.

有些问题建议创建一个自定义的SimpleDateFormat并将其应用到我试图做的opbject映射器,但是我想我错过了mapper.setDateFormat的正确语法(new SimpleDateFormat(“…”));

我试过用例如mapper.setDateFormat(new SimpleDateFormat(“/ Date(S)/”));

或者最后甚至是mapper.setDateFormat(new SimpleDateFormat(“SSSSSSSSSSSS)”));

但似乎这也不起作用,所以我现在没有想法,希望这里的一些人可以帮助我.

编辑1:

进一步调查,似乎一种方法是为杰克逊编写自定义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;
}

}

它实际上几乎返回我想要的东西,在模型中使用注释我的字段

@JsonDeserialize(using = JsonDateTimeDeserializer.class)

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

很明显,ZoneId在这里:

LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(unixTime),ZoneId.systemDefault());

是一个问题,除了错误的dateformat.

那么有人可以帮我解决如何切换到时间部分始终使用零并使我的日期格式正确吗?会很好!

解决方法

我假设数字591321600000是epoch milli(1970-01-01T00:00:00Z的毫秒数).

如果是这种情况,我认为SimpleDateFormat无法帮助你(至少我找不到使用这个类从epoch milli解析日期的方法).模式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());
    }
}

然后我将这个模块添加到我的mapper中并且它有效:

// 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 591321600000转换为Instant(表示UTC瞬间的类).以毫安计的591321600000相当于1988-09-27T00:00:00Z.

但这是UTC的日期/时间.要获得当地的日期和时间,您必须知道您所在的时区,因为在每个时区都有不同的日期和时间(世界上的每个人都在同一时刻,但他们的当地日期/时间可能会有所不同,具体取决于他们在哪里).

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

由于我的默认时区是America / Sao_Paulo,因此此代码将birthDate设置为1988-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)/

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读