当前位置: 首页 > 知识库问答 >
问题:

使用Jackson [duplicate]在Java 8中表示REST API请求中未定义的值

甄越
2023-03-14

我有一个PATCHRESTendpoint,公开了一个JSON接口,可用于根据主体中实际发送的属性部分更新实体。

让我们考虑一个表示实体的样本类:

class Account {
    private UUID accountUuid;
    private UUID ownerUuid;
    private String number;
    private String alias;

    // constructor, getters and setters omitted
}

其中,帐户Uuid所有者Uuid数字是必需的属性,别名是可选的。此外,我有一个用于更新所述帐户的命令类:

class UpdateAccountCommand {
    private UUID accountUuid;
    private String number;
    private String alias;

    // constructor, getters and setters omitted
}

对于我的PATCHendpoint,例如PATCH /accounts/{Account t-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
{}

对于所需的属性,这相当容易做到。使用杰克逊从 JSON 字符串反序列化到更新帐户命令实例后,我只需检查所需的属性是否为 null,当它不为 null 时,我更新给定的属性。

但是,使用可选属性会使情况复杂化,因为在这两种情况下,alias 属性都为 null

  • 当请求体显式将别名指定为null时,
  • alias属性根本没有在请求正文中指定时。

如何对这些可选属性进行建模,以便指示此可移动机制?

共有2个答案

高宇定
2023-03-14

您可以添加一个额外的布尔属性,该属性表示请求中是否存在可选属性

class UpdateAccountCommand {
  //...
  private String alias;
  @JsonIgnore
  private boolean isAliasSet;

  @JsonProperty
  public void setAlias(String value) {
     this.alias = value;
     this.isAliasSet = true;
  }
}

只有当“alias”存在时,才会调用setter,不管它是空的还是有值的

薛楷
2023-03-14

一个朴素的解决方案是引入某种包装器,它不仅包含原始值(例如,对于别名:字符串属性),还包含一个布尔值,指示是否在正文中指定了属性。这将要求您编写自定义反序列化机制,这可能是一项繁琐的工作。

由于问题是关于 Java 8 的,对于 Java 8 及更新版本,我建议使用可为空的可选,这与 Jackson 几乎开箱即用。

对于可选字段(可移除字段),您可以通过将原始值包装在optional:

class UpdateAccountCommand {
    private UUID accountUuid;
    private String number;
    private Optional<String> alias;

    // constructor, getters and setters omitted
}

为了让杰克逊与可选

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());

以下代码:

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.

 类似资料:
  • 我尝试了拉雷维尔的入门指南。通用域名格式。 有一章创建了任务。回调和函数

  • 我想知道,如何在Java8中使用流API迭代多级列表 根据Java8,我应该做如下的操作 我想要流利地做这件事(使用内部迭代)。任何解释都会很感激。

  • null 所以我想我有几个问题是基于我的发现。 如果不能重用流,那么何时返回流的实例以供以后使用? 是否可以克隆流,以便重用它们而不会导致?

  • 我有课。我需要在超时内做一些http工作。我面临的问题是超时内的http变量一直说它是未定义的。

  • 如果我更改了方法的名称,它会进行编译,但它不会重写toString,因此print方法不会打印预期的内容。 这是试图定义一个日志子系统,该子系统仅在需要时(当它真的要打印时)对lambda求值,但与非lambda参数兼容。我知道其他的方法来实现它,但我想知道为什么我不能这样做,如果有一个变通办法或我做错了什么,

  • 这是我的路由器 这是终端错误 正在端口3090上侦听。。。。。(节点:7764)未经处理的PromisejectionWarning:TypeError:无法读取/Users/macbook/Desktop/node project/server/routes/api/posts上未定义的属性“text”。js:19:24在ProcessTicks和Rejections(internal/proc