1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

スネークケースのJSONをJavaBeanに突っ込みたい

Posted at

先頭1文字だけのキャメルケースがうまくいかない?

メモ書き程度に、SpringのdependencyライブラリにあるJacksonで、スネークケースJSONの場合にうまくいかなかったので検証してみます。

ひとまず事象

SampleController.java
package com.example.controller;

import com.example.resource.TestJacksonResource;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class SampleController {

    @GetMapping("test")
    public TestJacksonResource jackson() throws IOException {
        
        String snakeJson = "{\"i_hoge_name\":\"hoge\",\"low_fuga_name\":\"fuga\"}";
        
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

        TestJacksonResource resource = objectMapper.readValue(snakeJson, TestJacksonResource.class);

        return resource;
    }
}
TestJacksonResource.java
package com.example.resource;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class TestJacksonResource implements Serializable {
    private String iHogeName;
    private String lowFugaName;
}
実行結果
Unrecognized field "i_hoge_name" (class com.example.resource.TestJacksonResource), not marked as ignorable (2 known properties: "ihoge_name", "low_fuga_name"]) at [Source: (String)"{"i_hoge_name":"hoge","low_fuga_name":"fuga"}"; line: 1, column: 17] (through reference chain: com.example.resource.TestJacksonResource["i_hoge_name"])
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "i_hoge_name" (class com.example.resource.TestJacksonResource), not marked as ignorable (2 known properties: "ihoge_name", "low_fuga_name"])
 at [Source: (String)"{"i_hoge_name":"hoge","low_fuga_name":"fuga"}"; line: 1, column: 17] (through reference chain: com.example.resource.TestJacksonResource["i_hoge_name"])
	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
	at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:823)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1153)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1589)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1567)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:294)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
	at com.example.controller.SampleController.jackson(SampleController.java:22)

Jacksonにより認識されているプロパティ名がどうやらi_hoge_nameではなくihoge_nameのためマッピングできないらしい。

JavaBeanのフィールド名に@JsonPropertyをつければ解決することはわかっているのですが、なるべくJavaBeanに手を入れたくないです。

JavaBeanのフィールドをpublicにする

通ったけどpublicなフィールドなんていやじゃ!

lombok使わないでprivateフィールドでアクセッサ用意してみる

TestJacksonResource.java
    public String getiHogeName() {
        return iHogeName;
    }

    public String getLowFugaName() {
        return lowFugaName;
    }

    public void setiHogeName(String iHogeName) {
        this.iHogeName = iHogeName;
    }

    public void setLowFugaName(String lowFugaName) {
        this.lowFugaName = lowFugaName;
    }
実行結果
{"iHogeName":"hoge","lowFugaName":"fuga"}

おぉ通った!!

lombokの自動生成クラスをのぞいてみる

TestJacksonResource.class
public String getIHogeName() {
    return this.iHogeName;
}

なるほど、これで原因はわかりましたね。
実装まではのぞいてませんが、Jacksonのプロパティ名を決定するロジックがアクセッサメソッドから生成するみたいですね。

iHogeNameのアクセッサ命名ってgetIHogeName(lombok)とgetiHogeName(intelliJのcommand+N)はどっちが正しいのでしょう。
感覚的には先頭大文字のgetIHogeNameかなぁ。。。

そもそもそんなフィールド名つけんじゃねぇー!が正解かも。

参考

こんなstackoverflowもありました。
Springbootの@JsonPropertyで指定したキーでJSONを返したい

挙動から察するに、Jacksonが「フィールド名」と、(lombokにより自動生成されている)「getter」の双方からJSONシリアライズ対象を決定しているように見受けられます(こちらによるとおそらくこの推測は正しいかと)。Spring上での設定は私には分からないのですが(前述リンク先の通り、JacksonへはObjectMapper#setVisibilityメソッドで設定可能)、フィールドアクセスのみでJSONシリアライズを行うようにすれば解消するのではないでしょうか。(Jackson2ObjectMapperBuilder bean を設定?)

原因はわかったけどlombok使いたいしフィールド名も変えたくない

そもそもの使い方が間違ってるから諦めますか、、、
時間があったら調べてみたいですが。

ただデシリアライズ(読み込み)時は@JsonPropertyでいいんですけど、シリアライズ時も影響を受けるので、いい方法無いかなぁ

1
2
1

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?