是否可以为一个特定的执行配置 ObjectMapper?
Is it possible to configure ObjectMapper for one specific execution?
我正在开发一个 Web 项目,它使用一个静态 ObjectMapper,它是通过 XML 文件配置的,并且应该在整个项目中生效。但是,我必须实现一个 API 来发送一个响应,无论设置如何,都不会忽略 null 属性 。我的老板告诉我他不希望创建另一个 ObjectMapper,并且创建我自己的 JSON writer 被认为是多余的,因此也被禁止。这导致我被困在这里。
我试过了。
Map<String, Object>resultMap = getResult();
try {
mapper.setSerializationInclusion(Include.ALWAYS);
response = mapper.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} finally {
if (ServiceConfig.isWriteNull()) {
mapper.setSerializationInclusion(Include.ALWAYS);
} else {
mapper.setSerializationInclusion(Include.NON_NULL);
}
}
临时切换设置,它有效。但是考虑到映射器是异步使用的,改变全局配置绝对不是一个好主意。我还考虑过在配置切回之前锁定映射器,但由于映射器是静态的,这可能是另一个坏主意。我想要一些简洁的方式,比如注释或参数神奇地影响单次执行。请问可以吗?
您当前拥有的是危险的,因为您正在临时更改全局映射器的配置。这也会影响同时使用同一个映射器实例进行序列化的其他线程。
但是,还有另一种方法可以实现您的需要。 ObjectMapper
实例有几种方法可以根据您的映射器创建 ObjectWriter
-实例。
Map<String, Object> resultMap = getResult();
try {
response = mapper
.writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
正如 @Stepan Stahlmann
在您的评论中所说,您还可以使用 ObjectMapper#copy()
方法基于全局实例创建一个临时的新 ObjectMapper
实例。想法是一样的:使用全局 ObjectMapper
作为配置目的的根并进行一些调整,以便它生成适合 API-Contract.
的 JSON
Map<String, Object> resultMap = getResult();
try {
response = mapper
.copy()
.setSerializationInclusion(Include.ALWAYS)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
不同的方法...
我能想到另一种方法,而且我很确定还有更多方法。
您可以将 resultMap
包装在 class 中,并带有一些注释,这些注释应否决映射器的默认行为:
package example;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Map;
// Your inclusion rule
@JsonInclude(JsonInclude.Include.ALWAYS)
public class ResponseWrapper {
private final Map<String, Object> response;
public ResponseWrapper(Map<String, Object> response) {
this.response = response;
}
// tells Jackson to use the this a the actual value (so you don't see this wrapper in the json)
@JsonValue
public Map<String, Object> getResponse() {
return this.response;
}
}
Map<String, Object> resultMap = getResult();
try {
response = mapper.writeValueAsString(new ResponseWrapper(resultMap));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
Map<String, Object>resultMap = getResult();
try {
response = mapper
.writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) { throw new RuntimeException(e);}
我正在开发一个 Web 项目,它使用一个静态 ObjectMapper,它是通过 XML 文件配置的,并且应该在整个项目中生效。但是,我必须实现一个 API 来发送一个响应,无论设置如何,都不会忽略 null 属性 。我的老板告诉我他不希望创建另一个 ObjectMapper,并且创建我自己的 JSON writer 被认为是多余的,因此也被禁止。这导致我被困在这里。 我试过了。
Map<String, Object>resultMap = getResult();
try {
mapper.setSerializationInclusion(Include.ALWAYS);
response = mapper.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} finally {
if (ServiceConfig.isWriteNull()) {
mapper.setSerializationInclusion(Include.ALWAYS);
} else {
mapper.setSerializationInclusion(Include.NON_NULL);
}
}
临时切换设置,它有效。但是考虑到映射器是异步使用的,改变全局配置绝对不是一个好主意。我还考虑过在配置切回之前锁定映射器,但由于映射器是静态的,这可能是另一个坏主意。我想要一些简洁的方式,比如注释或参数神奇地影响单次执行。请问可以吗?
您当前拥有的是危险的,因为您正在临时更改全局映射器的配置。这也会影响同时使用同一个映射器实例进行序列化的其他线程。
但是,还有另一种方法可以实现您的需要。 ObjectMapper
实例有几种方法可以根据您的映射器创建 ObjectWriter
-实例。
Map<String, Object> resultMap = getResult();
try {
response = mapper
.writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
正如 @Stepan Stahlmann
在您的评论中所说,您还可以使用 ObjectMapper#copy()
方法基于全局实例创建一个临时的新 ObjectMapper
实例。想法是一样的:使用全局 ObjectMapper
作为配置目的的根并进行一些调整,以便它生成适合 API-Contract.
Map<String, Object> resultMap = getResult();
try {
response = mapper
.copy()
.setSerializationInclusion(Include.ALWAYS)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
不同的方法...
我能想到另一种方法,而且我很确定还有更多方法。
您可以将 resultMap
包装在 class 中,并带有一些注释,这些注释应否决映射器的默认行为:
package example;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Map;
// Your inclusion rule
@JsonInclude(JsonInclude.Include.ALWAYS)
public class ResponseWrapper {
private final Map<String, Object> response;
public ResponseWrapper(Map<String, Object> response) {
this.response = response;
}
// tells Jackson to use the this a the actual value (so you don't see this wrapper in the json)
@JsonValue
public Map<String, Object> getResponse() {
return this.response;
}
}
Map<String, Object> resultMap = getResult();
try {
response = mapper.writeValueAsString(new ResponseWrapper(resultMap));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
Map<String, Object>resultMap = getResult();
try {
response = mapper
.writer(SerializationFeature.WRITE_NULL_MAP_VALUES)
.writeValueAsString(resultMap);
} catch (JsonProcessingException e) { throw new RuntimeException(e);}