使用Jackson表示java8中restapi请求中未定义的值
我有一个使用Jackson表示java8中restapi请求中未定义的值,java,java-8,jackson,undefined,Java,Java 8,Jackson,Undefined,我有一个补丁REST端点,它公开了一个JSON接口,可用于根据实体中实际发送的属性部分更新实体 让我们考虑一个表示实体的样本类: class Account { private UUID accountUuid; private UUID ownerUuid; private String number; private String alias; // constructor, getters and setters omitted } 其中,acc
补丁
REST端点,它公开了一个JSON接口,可用于根据实体中实际发送的属性部分更新实体
让我们考虑一个表示实体的样本类:
class Account {
private UUID accountUuid;
private UUID ownerUuid;
private String number;
private String alias;
// constructor, getters and setters omitted
}
其中,accountUuid
、owneruid
和number
是必需的属性,alias
是可选的。此外,我还有一个用于更新上述帐户的命令类:
class UpdateAccountCommand {
private UUID accountUuid;
private String number;
private String alias;
// constructor, getters and setters omitted
}
对于我的PATCH
端点,例如PATCH/accounts/{account uuid}
,我想实现一个功能,只更改请求中实际发送的属性:
// this should only update the account number
{"number": "123456"}
// this should only update the alias, by setting it to null
{"alias": null}
// this shouldn't update anything
{}
对于必需的属性,这相当容易做到。使用Jackson将JSON字符串反序列化为UpdateAccountCommand
实例后,我只需检查所需的属性是否为null
,当它不是null
时,我更新给定的属性
但是,由于别名
属性在这两种情况下均为null
,因此可选属性会使情况变得复杂:
- 当请求主体将
别名显式指定为null时
- 当请求正文中根本没有指定
属性时别名
如何对这些可选属性建模,以便指示这种可移动机制?一个简单的解决方案是引入某种包装器,它不仅包含原始值(例如,对于
别名:string
属性),还包含布尔值,指示是否在正文中指定了属性。这将需要您编写一个机制,这可能是一项乏味的工作
因为问题是关于Java8的,对于Java8和更新版本,我建议使用nullable,它在Jackson中几乎是开箱即用的
对于可选字段(可移动字段),可以通过将原始值包装在可选字段中来更改原始值:
class UpdateAccountCommand {
private UUID accountUuid;
private String number;
private Optional<String> alias;
// constructor, getters and setters omitted
}
以下代码:
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
String withNewAliasS = "{\"alias\":\"New Alias\"}";
String withNullAliasS = "{\"alias\":null}";
String withoutPropertyS = "{}";
UpdateAccountCommand withNewAlias = objectMapper.readValue(withNewAliasS, UpdateAccountCommand.class);
if (withNewAlias.getAlias() != null && withNewAlias.getAlias().isPresent()) {
System.out.println("withNewAlias specified new alias.");
}
UpdateAccountCommand withNullAlias = objectMapper.readValue(withNullAliasS, UpdateAccountCommand.class);
if (withNullAlias.getAlias() != null && !withNullAlias.getAlias().isPresent()) {
System.out.println("withNullAlias will remove an alias.");
}
UpdateAccountCommand withoutProperty = objectMapper.readValue(withoutPropertyS, UpdateAccountCommand.class);
if (withoutProperty.getAlias() == null) {
System.out.println("withoutProperty did not contain alias property on input at all.");
}
}
然后将其打印到控制台:
withNewAlias specified new alias.
withNullAlias will remove an alias.
withoutProperty did not contain alias property on input at all.
您可以添加一个额外的布尔属性,该属性表示请求中是否存在可选属性
class UpdateAccountCommand {
//...
private String alias;
@JsonIgnore
private boolean isAliasSet;
@JsonProperty
public void setAlias(String value) {
this.alias = value;
this.isAliasSet = true;
}
}
只有当“alias”存在时才会调用setter,无论它是null还是有值的使用映射而不是DTO?@Adrian这不是一个非常优雅的解决方案,因为唯一有效的映射是Map
,它不提供有关可用属性(键)及其类型的信息。没有什么能阻止任何人,例如,在你期望标量值的地方传递一个对象。我不认为这是一个很大的改进。不幸的是,这恰恰导致了我试图避免的情况,编写了不必要的代码,基本上污染了一个类,并使可选字段的大小增加了一倍。为了解决这个问题,需要引入一个包装器(正如我在自己的回答中提到的),您需要提供定制的反序列化机制,那么当后者提供了所需的机制时,CustomOptional
如何比Optional
更好呢?
class UpdateAccountCommand {
//...
private String alias;
@JsonIgnore
private boolean isAliasSet;
@JsonProperty
public void setAlias(String value) {
this.alias = value;
this.isAliasSet = true;
}
}