0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Jacksonで子オブジェクトのプロパティを親のものとして扱う

0
Posted at

springというかJacksonにおいて、Java側の子オブジェクトのプロパティを親階層のものとして扱いたい場合は@JsonUnwrappedを使用する。

たとえば、下記のようなjsonをJavaオブジェクト階層へ変換したい場合に使用する。

{
  "id": "id001",
  "start": "100",
  "end": "200"
}
@Data
public class MyRoot {
  String id;
  Range range;
}
@Data
public class Range {
  String start;
  String end;
}

環境

厳密にはJacksonだけの話だがおおむねspring-bootと一緒に使う事が大多数と思われるのでbuild.gradleをそのまま載せている。

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.9'
    id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

JsonUnwrappedの使い方

POJO

親オブジェクト側へプロパティを展開させたい子オブジェクトに@JsonUnwrappedを付与する。

@Data
public class MyRoot {

  String id;
  @JsonUnwrapped
  Range range;
}

上記に相当するjsonは以下となる。

{
  "id": "id001",
  "start": "100",
  "end": "200"
}

以下は動作確認用のサンプルコード。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SampleUnrapperdMain {

  public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    MyRoot value = mapper.readValue("""
        {
          "id": "id001",
          "start": "100",
          "end": "200"
        }
        """, MyRoot.class);
    System.out.println(value);
  }
}

以下が実行結果。

MyRoot(id=id001, range=Range(start=100, end=200))

record

recordも同様。ただ、俺自身がまだrecordの使用経験が少ないからもあるとは思うんだが、微妙に使い勝手が異なるように見える。

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonUnwrapped;

public record MyRootRecord(
    String id,
    @JsonUnwrapped
    @JsonIgnore
    RangeRecord range) {}
public record RangeRecord(String start, String end) {}

@JsonIgnoreを付与している点に注意。

以下は動作確認用のサンプルコード。

    MyRootRecord value1 = mapper.readValue("""
        {
          "id": "id001",
          "range": {
            "start": "100",
            "end": "200"
          }
        }
        """, MyRootRecord.class);
    System.out.println(value1);
    MyRootRecord value2 = mapper.readValue("""
        {
          "id": "id001",
          "start": "100",
          "end": "200"
        }
        """, MyRootRecord.class);
    System.out.println(value2);

以下が実行結果。

MyRootRecord[id=id001, range=RangeRecord[start=null, end=null]]
MyRootRecord[id=id001, range=RangeRecord[start=100, end=200]]

@JsonIgnoreを付与しない場合は両方とも同じ結果になってしまう。プロパティベースとコンストラクタベースでは微妙に使い勝手が異なる、のだろうか。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?