Java 使用 Jackson 映射时将默认值设置为空字段
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18805455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Setting default values to null fields when mapping with Hymanson
提问by gookman
I am trying to map some JSON objects to Java objects with Hymanson. Some of the fields in the JSON object are mandatory(which I can mark with @NotNull
) and some are optional.
我正在尝试使用 Hymanson 将一些 JSON 对象映射到 Java 对象。JSON 对象中的某些字段是强制性的(我可以用 标记@NotNull
),而有些是可选的。
After the mapping with Hymanson, all the fields that are not set in the JSON object will have a null value in Java. Is there a similar annotation to @NotNull
that can tell Hymanson to set a default value to a Java class member, in case it is null?
与Hymanson 映射后,所有未在JSON 对象中设置的字段在Java 中都会有一个空值。是否有类似的注释@NotNull
可以告诉 Hymanson 为 Java 类成员设置默认值,以防它为空?
Edit: To make the question more clear here is some code example.
编辑:为了使问题更清楚,这里有一些代码示例。
The Java object:
Java对象:
class JavaObject {
@NotNull
public String notNullMember;
@DefaultValue("Value")
public String optionalMember;
}
The JSON object can be either:
JSON 对象可以是:
{
"notNullMember" : "notNull"
}
or:
或者:
{
"notNullMember" : "notNull",
"optionalMember" : "optional"
}
The @DefaultValue
annotations is just to show what I am asking. It's not a real annotation. If the JSON object is like in the first example I want the value of the optionalMember
to be "Value"
and not null
. Is there an annotation that does such a thing?
该@DefaultValue
注释只是为了展示一下我问。这不是真正的注释。如果 JSON 对象与第一个示例中的一样,我希望 the 的值optionalMember
是"Value"
而不是null
。有没有做这样的事情的注释?
采纳答案by Ilya
There is no annotation to set default value.
You can set default value only on java class level:
没有注释来设置默认值。
您只能在 java 类级别设置默认值:
public class JavaObject
{
public String notNullMember;
public String optionalMember = "Value";
}
回答by gookman
Looks like the solution is to set the value of the properties inside the default constructor. So in this case the java class is:
看起来解决方案是在默认构造函数中设置属性的值。所以在这种情况下,java类是:
class JavaObject {
public JavaObject() {
optionalMember = "Value";
}
@NotNull
public String notNullMember;
public String optionalMember;
}
After the mapping with Hymanson, if the optionalMember
is missing from the JSON its value in the Java class is "Value"
.
与 Hymanson 映射后,如果optionalMember
JSON 中缺少 ,则其在 Java 类中的值为"Value"
.
However, I am still interested to know if there is a solution with annotations and without the default constructor.
但是,我仍然很想知道是否有带注释且不带默认构造函数的解决方案。
回答by Michael Conrad
Make the member private and add a setter/getter pair. In your setter, if null, then set default value instead. Additionally, I have shown the snippet with the getter also returning a default when internal value is null.
将成员设为私有并添加一个 setter/getter 对。在您的 setter 中,如果为 null,则改为设置默认值。此外,我还展示了带有 getter 的代码段,当内部值为 null 时也返回默认值。
class JavaObject {
private static final String DEFAULT="Default Value";
public JavaObject() {
}
@NotNull
private String notNullMember;
public void setNotNullMember(String value){
if (value==null) { notNullMember=DEFAULT; return; }
notNullMember=value;
return;
}
public String getNotNullMember(){
if (notNullMember==null) { return DEFAULT;}
return notNullMember;
}
public String optionalMember;
}
回答by Sergei Voitovich
Only one proposed solution keeps the default-value
when some-value:null
was set explicitly (POJO readability is lost there and it's clumsy)
只有一个建议的解决方案保持default-value
在some-value:null
设定明确(POJO可读性丢失那里,它的笨拙)
Here's how one can keep the default-value
and never set it to null
以下是如何保持default-value
并且永不将其设置为null
@JsonProperty("some-value")
public String someValue = "default-value";
@JsonSetter("some-value")
public void setSomeValue(String s) {
if (s != null) {
someValue = s;
}
}
回答by Adrian
I had a similar problem, but in my case the default value was in database. Below is the solution for that:
我有一个类似的问题,但在我的情况下,默认值在数据库中。以下是解决方案:
@Configuration
public class AppConfiguration {
@Autowired
private AppConfigDao appConfigDao;
@Bean
public Hymanson2ObjectMapperBuilder builder() {
Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder()
.deserializerByType(SomeDto.class,
new SomeDtoJsonDeserializer(appConfigDao.findDefaultValue()));
return builder;
}
Then in SomeDtoJsonDeserializer
use ObjectMapper
to deserialize the json and set default value if your field/object is null.
然后,在SomeDtoJsonDeserializer
使用ObjectMapper
反序列化JSON和设置默认值,如果你的领域/对象为null。
回答by Pau
You can create your own JsonDeserializer and annotate that property with @JsonDeserialize(as = DefaultZero.class)
您可以创建自己的 JsonDeserializer 并使用 @JsonDeserialize(as = DefaultZero.class)
For example: To configure BigDecimal to default to ZERO:
例如: 要将 BigDecimal 配置为默认为零:
public static class DefaultZero extends JsonDeserializer<BigDecimal> {
private final JsonDeserializer<BigDecimal> delegate;
public DefaultZero(JsonDeserializer<BigDecimal> delegate) {
this.delegate = delegate;
}
@Override
public BigDecimal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return jsonParser.getDecimalValue();
}
@Override
public BigDecimal getNullValue(DeserializationContext ctxt) throws JsonMappingException {
return BigDecimal.ZERO;
}
}
And usage:
和用法:
class Sth {
@JsonDeserialize(as = DefaultZero.class)
BigDecimal property;
}
回答by Wheezil
There are already a lot of good suggestions, but here's one more. You can use @JsonDeserialize to perform an arbitrary "sanitizer" which Hymanson will invoke post-deserialization:
已经有很多好的建议,但这里还有一个。您可以使用@JsonDeserialize 执行任意“清理程序”,Hymanson 将在反序列化后调用它:
@JsonDeserialize(converter=Message1._Sanitizer.class)
public class Message1 extends MessageBase
{
public String string1 = "";
public int integer1;
public static class _Sanitizer extends StdConverter<Message1,Message1> {
@Override
public Message1 convert(Message1 message) {
if (message.string1 == null) message.string1 = "";
return message;
}
}
}
回答by ptanov
Another option is to use InjectableValuesand @HymansonInject. It is very useful if you need to use not always the same value but one get from DB or somewhere else for the specific case. Here is an example of using HymansonInject
:
另一种选择是使用InjectableValues和@HymansonInject。如果您需要不总是使用相同的值,而是需要从 DB 或其他地方获取特定情况,这将非常有用。这是使用的示例HymansonInject
:
protected static class Some {
private final String field1;
private final String field2;
public Some(@JsonProperty("field1") final String field1,
@JsonProperty("field2") @HymansonInject(value = "defaultValueForField2",
useInput = OptBoolean.TRUE) final String field2) {
this.field1 = requireNonNull(field1);
this.field2 = requireNonNull(field2);
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
@Test
public void testReadValueInjectables() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues injectableValues =
new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
mapper.setInjectableValues(injectableValues);
final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
assertEquals(actualValueMissing.getField1(), "field1value");
assertEquals(actualValueMissing.getField2(), "somedefaultValue");
final Some actualValuePresent =
mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
assertEquals(actualValuePresent.getField1(), "field1value");
assertEquals(actualValuePresent.getField2(), "field2value");
}
Keep in mind that if you are using constructor to create the entity (this usually happens when you use @Valueor @AllArgsConstructorin lombok) and you put @HymansonInject
not to the constructor but to the property it will not work as expected - value of the injected field will always override value in json, no matter whether you put useInput = OptBoolean.TRUE
in @HymansonInject
. This is because Hymanson injects those properties after constructor is called (even if the property is final
) - field is set to the correct value in constructor but then it is overrided (check: https://github.com/FasterXML/Hymanson-databind/issues/2678and https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333for more information), this test is unfortunately passing:
请记住,如果你正在使用构造函数来创建实体(当你使用这个通常发生@Value或@AllArgsConstructor在龙目岛),你把@HymansonInject
不要的构造函数,但该财产为预计将无法正常工作-注入的价值现场将始终JSON覆盖值,不管你放useInput = OptBoolean.TRUE
的@HymansonInject
。这是因为 Hymanson 在调用构造函数后注入这些属性(即使该属性是final
) - 字段在构造函数中设置为正确的值,但随后被覆盖(检查:https: //github.com/FasterXML/Hymanson-databind/问题/2678和https://github.com/rzwitserloot/lombok/issues/1528#issuecomment-607725333有关更多信息),不幸的是,此测试已通过:
protected static class Some {
private final String field1;
@HymansonInject(value = "defaultValueForField2", useInput = OptBoolean.TRUE)
private final String field2;
public Some(@JsonProperty("field1") final String field1,
@JsonProperty("field2") @HymansonInject(value = "defaultValueForField2",
useInput = OptBoolean.TRUE) final String field2) {
this.field1 = requireNonNull(field1);
this.field2 = requireNonNull(field2);
}
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}
@Test
public void testReadValueInjectablesIncorrectBehavior() throws JsonParseException, JsonMappingException, IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues injectableValues =
new InjectableValues.Std().addValue("defaultValueForField2", "somedefaultValue");
mapper.setInjectableValues(injectableValues);
final Some actualValueMissing = mapper.readValue("{\"field1\": \"field1value\"}", Some.class);
assertEquals(actualValueMissing.getField1(), "field1value");
assertEquals(actualValueMissing.getField2(), "somedefaultValue");
final Some actualValuePresent =
mapper.readValue("{\"field1\": \"field1value\", \"field2\": \"field2value\"}", Some.class);
assertEquals(actualValuePresent.getField1(), "field1value");
// unfortunately "field2value" is overrided because of putting "@HymansonInject" to the field
assertEquals(actualValuePresent.getField2(), "somedefaultValue");
}
Hope this helps to someone with a similar problem.
希望这对有类似问题的人有所帮助。
P.S. I'm using Hymanson v. 2.9.6
PS 我正在使用 Hymanson v. 2.9.6