Jackson JSON 基于条件的日期格式序列化
Jackson JSON date format serialization based on condition
我正在使用 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() 方法,但这似乎只影响 headers。
@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-version 但它没有使用我的自定义序列化器,也没有使用新的基于注解的格式化程序(所以你不需要自定义序列化器)。确切地说是版本 1.9.11。
同样:问题是,我如何根据每条消息的 URL(更确切地说是从外部)的条件来控制日期格式。我在 webservice-method(第一个代码块)中知道我正在与哪个国家/地区交谈,但在序列化器中不知道...
结果
我在下面提供的解决方案确实可以解决我的问题,但我相信它不可能在 Mule 3.5.2 EE 中运行。但是,如果使用 Mule 3.6.0 或 3.7.0(这似乎是现在的最新版本),这可能是适合您以及其他可能使用其他框架的人的解决方案。
评论中没有提到,但我确实尝试注释掉 "String country = uriInfo.getPathParameters().getFirst("country");"并将国家/地区硬编码为 "no",我确实获得了挪威日期格式。当用 "se" 重新编译它时,我确实得到了瑞典语格式,所以这个解决方案确实有效,即使我无法让它工作。
更新2
我确实与 Mule Support 进行了讨论。在 3.5.x 的 Mule 和更早的版本中,jersey-json 和 jackson-jaxrs 被运送并且它加载有点随机(并且取决于不同的环境)。可以从 $MULE_HOME/lib/opt 中删除 jersey-json。 3.6.x及以后将只发货jackson-jaxrs.
因为我坐在一个有很多工作流程的系统上,所以我没有时间测试删除 jersey-json 是否不会破坏任何东西(因为删除文件会影响所有流程,而不仅仅是这个流程).基本上 3.6.x 及更高版本将更好地控制 Jersey(选择提供者等),并使它能够正常工作。
"...how can I control the date format based on conditions from for instance the URL (more exactly from the outside) for each message"
虽然需要多做一些工作,但一种方法是为每种类型的请求创建不同的 ObjectMapper
配置。为了确定将使用哪一个,我们可以在 ContextResolver
. We could inject a 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
我正在使用 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() 方法,但这似乎只影响 headers。
@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-version 但它没有使用我的自定义序列化器,也没有使用新的基于注解的格式化程序(所以你不需要自定义序列化器)。确切地说是版本 1.9.11。
同样:问题是,我如何根据每条消息的 URL(更确切地说是从外部)的条件来控制日期格式。我在 webservice-method(第一个代码块)中知道我正在与哪个国家/地区交谈,但在序列化器中不知道...
结果
我在下面提供的解决方案确实可以解决我的问题,但我相信它不可能在 Mule 3.5.2 EE 中运行。但是,如果使用 Mule 3.6.0 或 3.7.0(这似乎是现在的最新版本),这可能是适合您以及其他可能使用其他框架的人的解决方案。
评论中没有提到,但我确实尝试注释掉 "String country = uriInfo.getPathParameters().getFirst("country");"并将国家/地区硬编码为 "no",我确实获得了挪威日期格式。当用 "se" 重新编译它时,我确实得到了瑞典语格式,所以这个解决方案确实有效,即使我无法让它工作。
更新2
我确实与 Mule Support 进行了讨论。在 3.5.x 的 Mule 和更早的版本中,jersey-json 和 jackson-jaxrs 被运送并且它加载有点随机(并且取决于不同的环境)。可以从 $MULE_HOME/lib/opt 中删除 jersey-json。 3.6.x及以后将只发货jackson-jaxrs.
因为我坐在一个有很多工作流程的系统上,所以我没有时间测试删除 jersey-json 是否不会破坏任何东西(因为删除文件会影响所有流程,而不仅仅是这个流程).基本上 3.6.x 及更高版本将更好地控制 Jersey(选择提供者等),并使它能够正常工作。
"...how can I control the date format based on conditions from for instance the URL (more exactly from the outside) for each message"
虽然需要多做一些工作,但一种方法是为每种类型的请求创建不同的 ObjectMapper
配置。为了确定将使用哪一个,我们可以在 ContextResolver
. We could inject a 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