Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Jackson表示java8中restapi请求中未定义的值_Java_Java 8_Jackson_Undefined - Fatal编程技术网

使用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;
  }
}