1
1

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.

Enum を JSON オブジェクトとして変換する際にPropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES を指定するとdeclaring_classプロパティが削除されない

Posted at

何を言っているのか分からねーと思うが、俺も何が起きたのか分からなかった...

以下のようなEnum1JSON Objectとして変換すると・・・

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Planet {

    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6),
    MARS(6.421e+23, 3.3972e6),
    JUPITER(1.9e+27, 7.1492e7),
    SATURN(5.688e+26, 6.0268e7),
    URANUS(8.686e+25, 2.5559e7),
    NEPTUNE(1.024e+26, 2.4746e7);

    private final double mass; // in kilograms
    private final double radius; // in meters

    private Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }

    // universal gravitational constant (m3 kg-1 s-2)
    public static final double G = 6.67300E-11;

    public double getMass() {
        return mass;
    }

    public double getRadius() {
        return radius;
    }

    @JsonProperty
    public double surfaceGravity() {
        return G * mass / (radius * radius);
    }
}

以下のような出力になります。

{"mass":1.9E27,"radius":7.1492E7,"surfaceGravity":24.80617666947324}

デフォルトがキャメルケースなので、PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES を指定してスネークケースで出力されるようにしてみます。
すると・・・

ObjectMapper mapper = new ObjectMapper()
    .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
String json = mapper.writeValueAsString(Planet.JUPITER);
System.out.println(json);
{"mass":1.9E27,"radius":7.1492E7,"surface_gravity":24.80617666947324,"declaring_class":"sample.jackson.Planet"}

このようにスネークケースにはなるのですが、declaring_class というプロパティが出力されるようになってしまいます。

一応、BasicSerializerFactory#buildEnumSerializer(SerializationConfig, JavaType, BeanDescription) で、拾ってきてしまったjava.lang.Enum#getDeclaringClass()を除外しようとしているみたいなのですが・・・

com.fasterxml.jackson.databind.ser.BasicSerializerFactory.java

/* As per [Issue#24], may want to use alternate shape, serialize as JSON Object.
 * Challenge here is that EnumSerializer does not know how to produce
 * POJO style serialization, so we must handle that special case separately;
 * otherwise pass it to EnumSerializer.
 */
JsonFormat.Value format = beanDesc.findExpectedFormat(null);
if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) {
    // one special case: suppress serialization of "getDeclaringClass()"...
    ((BasicBeanDescription) beanDesc).removeProperty("declaringClass");
    // returning null will mean that eventually BeanSerializer gets constructed
    return null;
}

このようにキャメルケースでベタ書きしています。
スネークケースを指定した場合にココか動いていないようにも見えるので、非常に不安になるコードです。

com.fasterxml.jackson.databind.introspect.BasicBeanDescription.java

/**
 * Method that can be used to prune unwanted properties, during
 * construction of serializers and deserializers.
 * Use with utmost care, if at all...
 * 
 * @since 2.1
 */
public boolean removeProperty(String propName)
{
    Iterator<BeanPropertyDefinition> it = _properties.iterator();
    while (it.hasNext()) {
        BeanPropertyDefinition prop = it.next();
        if (prop.getName().equals(propName)) {
            it.remove();
            return true;
        }
    }
    return false;
}

・・・うーん、BeanPropertyDefinition#getName() と比較していますが、今回の場合だと "declaringClass" ではなく "declaring_class" が設定されています。まさかとは思いましたが、結構単純なミスじゃないかと思われます。
あえて "declaringClass" を指定したということはStrategyに左右されなそうなBeanPropertyDefinition#getInternalName() を使うべきだったのではないかと。。。

com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition.java

/**
 * Accessor that can be used to determine implicit name from underlying
 * element(s) before possible renaming. This is the "internal"
 * name derived from accessor ("x" from "getX"), and is not based on
 * annotations or naming strategy.
 */
public abstract String getInternalName();

  1. ORACLE The Java™ Tutorials Enum Types

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?