はじめに
- 今回は表題の通りです
- 割とTips的な投稿です
背景
- Apiを作っていた際に、中身がnullのフィールドがあったときにResponseからキー名ごと落とす方法はないのかなと思ったのでちょっと調査したことをまとめておく
用意したもの
- Java1.8
- あとはよしなに
早速
- 適当なDTOクラスをつくってみる
HelloDto.java
public class HelloDto {
private String name;
private Integer age;
private String address;
// Getterとコンストラクタは省略
}
- 適当に呼び出す
HelloController.java
public class HelloController {
public HelloDto getHello() {
return new HelloDto("tom", 21, null);
}
}
- この際、HelloDtoのResponseを取得してみるとこんな感じになってる
hello.json
{
"name": "tom",
"age": 21,
"address": null
}
- この場合に、addressをキー名ごと落としたいとしたらどうするか?
- ちなみに引数違いのコンストラクタを用意しても、結果的にResponseにnullが詰められて返却されてないのでだめでした。
解決策
- jacksonのアノテーションを使って解決する
- 具体的には
@JsonInclude(JsonInclude.Include.NON_NULL)
を使うだけ
HelloDto.java
public class HelloDto {
private String name;
private Integer age;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String address;
// Getterとコンストラクタは省略
}
- 無事に消えた
hello.json
{
"name": "tom",
"age": 21
}
- ちなみに、
@JsonInclude(JsonInclude.Include)
にはNON_NULL
とNON_EMPTY
があって(他にもあります)、Javadocを読んでみると
JsonInclude.java
/**
* Value that indicates that only properties with non-null
* values are to be included.
*/
NON_NULL,
/**
* Value that indicates that only properties with null value,
* or what is considered empty, are not to be included.
* Definition of emptiness is data type specific; see below
* for details on actual handling.
*<p>
* Default emptiness for all types includes:
*<ul>
* <li><code>Null</code> values.</li>
* <li>"Absent" values (see {@link #NON_ABSENT})</li>
*</ul>
* so that as baseline, "empty" set includes values that would be
* excluded by both {@link #NON_NULL} and {@link #NON_ABSENT}.
*<br>
* Beyond this base, following types have additional empty values:
*<ul>
* <li>For {@link java.util.Collection}s and {@link java.util.Map}s,
* method <code>isEmpty()</code> is called;
* </li>
* <li>For Java arrays, empty arrays are ones with length of 0
* </li>
* <li>For Java {@link java.lang.String}s, <code>length()</code> is called,
* and return value of 0 indicates empty String
* </li>
* </ul>
* and for other types, null values are excluded but other exclusions (if any).
*<p>
* Note that this default handling can be overridden by custom
* <code>JsonSerializer</code> implementation: if method <code>isEmpty()</code>
* is overridden, it will be called to see if non-null values are
* considered empty (null is always considered empty).
*<p>
* Compatibility note: Jackson 2.6 included a wider range of "empty" values than
* either earlier (up to 2.5) or later (2.7 and beyond) types; specifically:
*<ul>
* <li>Default values of primitive types (like <code>0</code> for `int`/`java.lang.Integer`
* and `false` for `bool`/`Boolean`)
* </li>
* <li>Timestamp 0 for date/time types
* </li>
*</ul>
* With 2.7, definition has been tightened back to only containing types explained
* above (null, absent, empty String, empty containers), and now
* extended definition may be specified using {@link #NON_DEFAULT}.
*/
NON_EMPTY,
- NON_EMPTYの方が、NON_NULLの判定条件に加えて、nullっぽい時も弾いてくれるかつ、コレクションでもいけるよってのことでした。なので、nullだけ弾きたいというケース以外は、NON_EMPTYでいいんじゃないかと・・・。
- ちなみに、各フィールドにつけなくても、クラスアノテーションとして使ってもよしなにやってくれます
HelloDto.java
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class HelloDto {
private String name;
private Integer age;
private String address;
private List<String> friends;
public HelloDto(String name, Integer age, String address, List<String> friends) {
this.name = name;
this.age = age;
this.address = address;
this.friends = friends;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public String getAddress() {
return address;
}
public List<String> getFriends() {
return friends;
}
}
- 呼び元こんな感じ
HelloController.java
public class HelloController {
public HelloDto getHello() {
return new HelloDto("tom", 21, null, new ArrayList<>());
}
}
- で、Responseはこんな感じ
hello.json
{
"name": "tom",
"age": 21
}
- 無事に効いた(⌒▽⌒)
まとめ
- @JsonIncludeは便利
- ユースケースは少ないかもしれないけど、BFFとかの場合にFEとしてはnull返されるならキー名ごと落として欲しいみたいな時は役にたつかも