【问题标题】:Jackson JSON date format serialization based on condition基于条件的Jackson JSON日期格式序列化
【发布时间】:2015-08-05 08:25:31
【问题描述】:

我正在使用 Mule 3.5.2,并且我有一个发送和接收 JSON 消息的 REST 服务。该服务适用于挪威和瑞典。所有日期都以字符串形式发送,但瑞典和挪威的格式不同。我通过 URL 知道哪个国家/地区调用我们的服务。我正在使用自定义日期序列化器和反序列化器。

在接收 JSON 消息时我可以作弊,格式不同,在我的自定义反序列化器中我可以尝试一种格式。如果失败了,我就试试另一个。但是:如何以正确的格式进行序列化?

似乎没有任何方法可以向序列化程序发送参数,表明此特定消息将发送到挪威,因此请使用此日期格式...而下一个发送到瑞典则使用另一种格式等。

我拥有的代码,可能会有所帮助:

@GET
@Path("{country:se|no}/{id}")
public Response webservice(@PathParam("country") String country,
        @PathParam("id") String id) {
    country = country.toUpperCase();
    WebServiceResponse response = doWebServiceStuff(id, country)
    return Response.ok(reponse).build();
}

Response 有一个 .language() 方法,但这似乎只会影响标头。

@JsonAutoDetect
public class WebServiceResponse {

    @JsonSerialize(using = JsonDateSerializer.class)
    @JsonDeserialize(using = JsonDateDeserializer.class)
    private Date date;

    public void setDate(Date d) { this.date = d; }
    public Date getDate() { return this.date; }
}

今天的序列化器。我希望它能够适应挪威用户或瑞典用户。

public class JsonDateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

@Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
        String formattedDate = dateFormat.format(date);
        gen.writeString(formattedDate);
        provider.getConfig().getDateFormat()
    }
}

反序列化器。它也有同样的问题,但我可以用 try/catch 包围它...如果瑞典日期格式无效,请尝试使用挪威数字进行解析,如果仍然有问题则抛出 RuntimeException。

public class JsonDateDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser parser, DeserializationContext deserializationContext)
        throws IOException, JsonProcessingException {

        String dateText = parser.getText();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        try {
            return dateFormat.parse(dateText);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            throw new RuntimeException("Can't parse date " + dateText, e);
        }
    }

}

顺便说一句...我正在使用 Jackson 的 codehaus 版本,因为它似乎适用于 Mule。我尝试了 FasterXML 版本,但没有使用我的自定义序列化程序,也没有使用新的基于注释的格式化程序(所以你不需要自定义序列化程序)。确切地说是 1.9.11 版。

再次重申:问题是,我如何根据每条消息的 URL(更准确地说是外部)等条件控制日期格式。我在 webservice-method(第一个代码块)中知道我正在与哪个国家/地区交谈,但在序列化程序中不知道...

结果

我在下面提供的解决方案确实可以解决我的问题,但我相信它无法在 Mule 3.5.2 EE 中运行。但是,如果使用 Mule 3.6.0 或 3.7.0(现在似乎是最新版本),这可能是您以及可能使用其他框架的其他人的解决方案。

cmets 中没有提到,但我确实尝试注释掉“String country = uriInfo.getPathParameters().getFirst("country");”并将国家硬编码为“否”,我确实得到了挪威日期格式。当用“se”重新编译它时,我确实得到了瑞典语格式,所以即使我无法让它工作,这个解决方案也确实有效。

更新2

我确实与 Mule Support 进行了讨论。在 Mule 和更早版本的 3.5.x 中,发布了 jersey-json 和 jackson-jaxrs,它加载的有点随机(并且取决于不同的环境)。可以从 $MULE_HOME/lib/opt 中删除 jersey-json。 3.6.x 及更高版本将仅发布 jackson-jaxrs。

当我坐在一个有许多工作流的系统上时,我没有时间测试删除 jersey-json 是否不会破坏任何东西(因为删除文件会影响所有流,而不仅仅是这个)。基本上 3.6.x 及更高版本将对 Jersey 有更好的控制(选择提供者等),并且可以使其正常工作。

【问题讨论】:

    标签: java jersey jackson mule


    【解决方案1】:

    “...如何根据条件控制日期格式,例如每条消息的 URL(更准确地说是来自外部)”

    虽然需要做更多的工作,但一种方法是为每种类型的请求创建不同的 ObjectMappers 配置。要确定将使用哪一个,我们可以在ContextResolver 中做出决定。我们可以将UriInfo 注入解析器,以获取@PathParam("country") 的值。然后从中做出决定,将使用哪个映射器。例如

    @Provider
    public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
        
        private final ObjectMapper sweMapper;
        private final ObjectMapper norMapper;
        private final ObjectMapper defaultMapper;
        
        @Context
        private UriInfo uriInfo;
        
        public ObjectMapperContextResolver() {
            defaultMapper = new ObjectMapper();
            
            sweMapper = new ObjectMapper();
            SimpleModule sweModule = new SimpleModule("SweModule", new Version(1,0,0,null));
            sweModule.addDeserializer(Date.class, new JsonDateDeserializer(sweFormat));
            sweModule.addSerializer(Date.class, new JsonDateSerializer(sweFormat));
            sweMapper.registerModule(sweModule);
            
            norMapper = new ObjectMapper();
            SimpleModule norModule = new SimpleModule("NorModule", new Version(1,0,0,null));
            norModule.addDeserializer(Date.class, new JsonDateDeserializer(norFormat));
            norModule.addSerializer(Date.class, new JsonDateSerializer(norFormat));
            norMapper.registerModule(norModule);
        }
    
        @Override
        public ObjectMapper getContext(Class<?> type) {
            String country = uriInfo.getPathParameters().getFirst("country");
            if (country == null) {
                return defaultMapper;
            }
            
            switch (country) {
                case "se": return sweMapper;
                case "no": return norMapper;
                default: return defaultMapper;
            }
        }  
    }
    

    我们使用三个映射器的原因是为了一个,它们的创建成本很高。其次,配置它们不是线程安全的。并且由于 ContextResolver 将是一个单例,因此只有一个映射器将用于应用程序。所以我们只为不同的情况创建三个。

    如果你走这条路,你还应该记得从字段中删除序列化注释。


    更新

    所以对于 Jersey 2.6,上述解决方案似乎存在问题。它只是在启动时失败。我能找到的解决方案是使用这个依赖

    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>${jersey-version}</version>
    </dependency>
    

    似乎加载此模块的某些部分会导致它失败。相反,只需使用 Pure Jackson 依赖项(上面实际上会引入并使用它自己)。

    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-jaxrs</artifactId>
        <version>1.9.13</version>
    </dependency>
    

    注意: jersey-json:1.6 使用上述依赖的 1.7.1。我刚刚切换到使用最新的 1.x 版本。所以你可能想也可能不想把它切换回来。

    摆脱您可能拥有的旧工件,即

    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    

    并将Jackson包添加为要扫描的包

    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>
            com.your.packages,
            org.codehaus.jackson.jaxrs
        </param-value>
    </init-param>
    

    或者,如果您使用某些特定于 Mule 的配置,只需注册这些

    • org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider
    • org.codehaus.jackson.jaxrs.JacksonMappingExceptionMapper
    • org.codehaus.jackson.jaxrs.JacksonParseExceptionMapper

    【讨论】:

    • 非常好的解决方案,但是在运行它时我收到了一个异常 (gist.github.com/aquaplanet/4f32288a7da8a76976d4)。在我的 Gist 中查找第 88 行。它指向“String country = uriInfo.getPathParameters().getFirst("country");”所以我认为@Context 不起作用。我注释掉了序列化注释,添加了 并且几乎复制/粘贴了您的代码。确保我的序列化程序采用了构造函数的格式。感谢您的帮助,这是我未知的领域。
    • 嗯.. 您使用的是哪个 Jersey 版本?我用 1.19 进行了测试。也许在更高版本中实现了将 UriIinfo(因为它的请求范围)注入单例的能力。
    • 你可能是对的。 Mule 随附 Jersey:1.6 2011 年 3 月 25 日下午 1:14,所以它并不是最新最好的。我现在正试图强迫 Mule 使用 jersey-bundle-1.19.jar,但它比听起来更难......我稍后会报告它的进展情况。
    • 在你尝试之前让我测试一下。我认为可能有一些解决方案。给我几分钟时间测试
    • 所以我找到了一个我已经测试过并且有效的解决方案。没有经过实战测试,但希望你没有任何问题。我会发布更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 2017-02-14
    • 2020-04-13
    相关资源
    最近更新 更多