1

以下を含むポリモーフィック型のGenson 1.3 を使用して、Java で JSON シリアル化を実装しようとしています。

  • Numbers
  • 配列
  • Enumクラス

以下の SSCCE は、私が達成しようとしていることを大まかに示しています。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;

/**
 * A Short, Self Contained, Compilable, Example for polymorphic serialization
 * and deserialization.
 */
public class GensonPolymoprhicRoundTrip {

    // our example enum
    public static enum RainState {
        NO_RAIN,
        LIGHT_RAIN,
        MODERATE_RAIN,
        HEAVY_RAIN,
        LIGHT_SNOW,
        MODERATE_SNOW,
        HEAVY_SNOW;
    }

    public static class Measurement<T> {
        public T value;
        public int qualityValue;
        public String source;

        public Measurement() {
        }
        public Measurement(T value, int qualityValue, String source) {
            this.value = value;
            this.qualityValue = qualityValue;
            this.source = source;
        }
    }

    public static class DTO {
        public List<Measurement<?>> measurements;

        public DTO(List<Measurement<?>> measurements) {
            this.measurements = measurements;
        }
    }

    public static void main(String... args) {
        Genson genson = new GensonBuilder()
        .useIndentation(true)
        .useRuntimeType(true)
        .useClassMetadataWithStaticType(false)
        .addAlias("RainState", RainState.class)
        .useClassMetadata(true)
        .create();

        DTO dto = new DTO(
                new ArrayList(Arrays.asList(
                        new Measurement<Double>(15.5, 8500, "TEMP_SENSOR"),
                        new Measurement<double[]>(new double[] {
                                2.5,
                                1.5,
                                2.0
                        }, 8500, "WIND_SPEED"),
                        new Measurement<RainState>(RainState.LIGHT_RAIN, 8500, "RAIN_SENSOR")
                        )));
        String json = genson.serialize(dto);
        System.out.println(json);
        DTO deserialized = genson.deserialize(json, DTO.class);
    }
}

Numbers と Arrays はそのままで問題なく機能しましたが、enum クラスには少し問題がありました。この場合、シリアル化された JSON フォームは、次のものを含む IMO JSON オブジェクトである必要があります。

  • タイプメンバー
  • バリューメンバー

クラスを見るEnumConverterと、カスタムを提供する必要があることがわかりますConverterただし、逆シリアルConverter化中に呼び出されるように を適切に登録する方法がよくわかりません。このシリアル化は、Genson を使用してどのように解決する必要がありますか?

4

1 に答える 1

1

完全な例を提供するのに最適です。

最初の問題は、DTO には引数のないコンストラクターがないことですが、Genson は引数を持つコンストラクターでもクラスをサポートします。「useConstructorWithArguments(true)」を使用してビルダーを介して有効にするだけです。

ただし、これで問題が完全に解決するわけではありません。現時点では、Genson は json オブジェクトとしてシリアル化された型に対してのみ完全なポリモーフィック サポートを提供しています。Genson が「@class」というプロパティを追加するためです。そのための未解決の問題があります。

おそらく、ほとんどの状況で機能する最善の解決策は、json オブジェクトのすべての値を自動的にラップするコンバーターを定義することです。これにより、クラス メタデータを処理するコンバーターがそれを生成できるようになります。これは、Genson によって公式にサポートされるのを待っている間は、「十分な」解決策になる可能性があります。

したがって、最初にラッピングコンバーターを定義します

public static class LiteralAsObjectConverter<T> implements Converter<T> {
    private final Converter<T> concreteConverter;

    public LiteralAsObjectConverter(Converter<T> concreteConverter) {
        this.concreteConverter = concreteConverter;
    }

    @Override
    public void serialize(T object, ObjectWriter writer, Context ctx) throws Exception {
        writer.beginObject().writeName("value");
        concreteConverter.serialize(object, writer, ctx);
        writer.endObject();
    }

    @Override
    public T deserialize(ObjectReader reader, Context ctx) throws Exception {
        reader.beginObject();
        T instance = null;
        while (reader.hasNext()) {
            reader.next();
            if (reader.name().equals("value")) instance = concreteConverter.deserialize(reader, ctx);
            else throw new IllegalStateException(String.format("Encountered unexpected property named '%s'", reader.name()));
        }
        reader.endObject();
        return instance;
    }
}

次に、それを ChainedFactory に登録する必要があります。これにより、デフォルトのコンバーターに委任できるようになります (この方法では、他のタイプで自動的に機能します)。

Genson genson = new GensonBuilder()
            .useIndentation(true)
            .useConstructorWithArguments(true)
            .useRuntimeType(true)
            .addAlias("RainState", RainState.class)
            .useClassMetadata(true)
            .withConverterFactory(new ChainedFactory() {
                @Override
                protected Converter<?> create(Type type, Genson genson, Converter<?> nextConverter) {
                    if (Wrapper.toAnnotatedElement(nextConverter).isAnnotationPresent(HandleClassMetadata.class)) {
                      return new LiteralAsObjectConverter(nextConverter);
                    } else {
                      return nextConverter;
                    }
                }
            }).create();

このソリューションの欠点は、useClassMetadataWithStaticType を true に設定する必要があることです...しかし、それは最適であり、修正できるため、許容できると思いますが、Gensons コードのいくつかの変更を意味し、残りは引き続き機能します。

この問題に興味をお持ちの場合は、その問題に挑戦し、Genson の一部としてこの機能を提供するために PR を開いてください。

于 2015-11-06T23:35:32.523 に答える