小编典典

部分GSON反序列化

java

JsonDeserializer由于要执行处理的业务逻辑,因此我正在实现自定义。但是某些部分可以解析为标准方式。这是否可能-
我自己处理一些元素,并让一些嵌套元素自动处理?

这是JSON:

{
  "id": "10",
  "games": ["PZ"],
  "definition":
  {
    "count": 10,
    "operatorDefinitions": [
      {
        "operators": "+",
        "first": "1-5",
        "second": "1-5",
        "result": "2-5"
      }
    ]
  }

这是definition项的自定义反序列化器:

public class FormulaDefinitionGsonAdapter implements JsonDeserializer<FormulaDefinition> {
public FormulaDefinition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    FormulaDefinition definition = new FormulaDefinition();
    JsonObject jsonObject = json.getAsJsonObject();
    JsonPrimitive p = jsonObject.getAsJsonPrimitive("count");
    definition.setCount(p.getAsInt());

    JsonArray array = jsonObject.getAsJsonArray("operatorDefinitions");
    if (array == null || array.size() == 0) {
        throw new JsonParseException("Element 'operatorDefinitions' is missing!");
    }

    for (JsonElement jsonElement : array) {
        jsonObject = (JsonObject) jsonElement;
        p = jsonObject.getAsJsonPrimitive("first");
        String sValues = p.getAsString();
        Values firstArgValues = Values.parse(sValues);

现在,我想让GSON解析operators枚举。我可以自己完成,这只是几行代码,但是我更愿意库做很多。


阅读 281

收藏
2020-11-30

共1个答案

小编典典

…但是我希望图书馆尽其所能。

好吧,只需使用Gson。

数据传输对象模式,尤其是Gson映射类,可以完美地解决您的问题。默认情况下,如果Gson能够满足内置功能的映射,并且除了特殊情况外,您不必自己完成工作。此类映射类仅旨在存在于JSON内容和您的业务对象类之间,以便对数据进行(反)序列化(简单地说,DTO仅为此目的而存在,并且与Gson相关的注释不得散布到您的业务类中-
只需将DTO转换为业务对象)。

对应

final class Wrapper {

    @SerializedName("id")
    @Expose
    private final String id = null;

    @SerializedName("games")
    @Expose
    private final List<String> games = null;

    @SerializedName("definition")
    @Expose
    private final FormulaDefinition formulaDefinition = null;

    private Wrapper() {
    }

    @Override
    public String toString() {
        return new StringBuilder("Wrapper{")
                .append("id='").append(id)
                .append("', games=").append(games)
                .append(", formulaDefinition=").append(formulaDefinition)
                .append('}')
                .toString();
    }

}



package q41323887;

import java.util.List;

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

final class FormulaDefinition {

    @SerializedName("count")
    @Expose
    private /*final*/ int count /*= 0*/; // Gson works with final primitives like `int` strangely

    @SerializedName("operatorDefinitions")
    @Expose
    private final List<OperatorDefinition> operatorDefinitions = null;

    private FormulaDefinition() {
    }

    @Override
    public String toString() {
        return new StringBuilder("FormulaDefinition{")
                .append("count=").append(count)
                .append(", operatorDefinitions=").append(operatorDefinitions)
                .append('}')
                .toString();
    }

}



final class OperatorDefinition {

    @SerializedName("operators")
    @Expose
    private final Operator operators = null;

    @SerializedName("first")
    @Expose
    private final String first = null;

    @SerializedName("second")
    @Expose
    private final String second = null;

    @SerializedName("result")
    @Expose
    private final String result = null;

    private OperatorDefinition() {
    }

    @Override
    public String toString() {
        return new StringBuilder("OperatorDefinition{")
                .append("operators=").append(operators)
                .append(", first='").append(first)
                .append("', second='").append(second)
                .append("', result='").append(result)
                .append("'}")
                .toString();
    }

}



enum Operator {

    PLUS("+"),
    MINUS("-"),
    ASTERISK("*"),
    SLASH("/");

    private static final Map<String, Operator> tokenToOperatorIndex = createTokenToOperatorIndexInJava8();

    private final String token;

    Operator(final String token) {
        this.token = token;
    }

    static Operator resolveOperator(final String token)
            throws NoSuchElementException {
        final Operator operator = tokenToOperatorIndex.get(token);
        if ( operator == null ) {
            throw new NoSuchElementException("Cannot resolve operator by " + token);
        }
        return operator;
    }

    private static Map<String, Operator> createTokenToOperatorIndex() {
        final Map<String, Operator> index = new HashMap<>();
        for ( final Operator operator : values() ) {
            index.put(operator.token, operator);
        }
        return unmodifiableMap(index);
    }

    private static Map<String, Operator> createTokenToOperatorIndexInJava8() {
        final Map<String, Operator> index = Stream.of(values())
                .collect(toMap(operator -> operator.token, identity()));
        return unmodifiableMap(index);
    }

}

反序列化

然后,由于您本operators打算成为有效的枚举,因此这是您真正需要自定义JSON反序列化器的唯一地方,仅因为Gson默认规则不了解这些规则。

final class OperatorJsonDeserializer
        implements JsonDeserializer<Operator> {

    private static final JsonDeserializer<Operator> operatorJsonDeserializer = new OperatorJsonDeserializer();

    private OperatorJsonDeserializer() {
    }

    static JsonDeserializer<Operator> getOperatorJsonDeserializer() {
        return operatorJsonDeserializer;
    }

    @Override
    public Operator deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        try {
            final String token = json.getAsJsonPrimitive().getAsString();
            return resolveOperator(token);
        } catch ( final NoSuchElementException ex ) {
            throw new JsonParseException(ex);
        }
    }

}

演示版

现在,您可以使用Wrapper该类反序列化JSON:

// Gson instances are thread-safe and can be easily instantiated once
private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Operator.class, getOperatorJsonDeserializer())
        .create();

public static void main(final String... args)
        throws IOException {
    try ( final Reader reader = new InputStreamReader(EntryPoint.class.getResourceAsStream("/test.json")) ) {
        final Wrapper wrapper = gson.fromJson(reader, Wrapper.class);
        out.println(wrapper);
        // ... convert the wrapper DTO above to your target business object
    }
}

输出:

包装器{id = ‘10’,游戏= [PZ],FormulaDefinition = FormulaDefinition {count =
10,operatorDefinitions = [OperatorDefinition {operators = PLUS,first
=‘1-5’,second =‘1-5’,result =’ 2-5’}]}}


编辑

我在以下代码片段中对Gson的理解是错误的:

    @SerializedName("count")
    @Expose
    private /*final*/ int count /*= 0*/; // Gson works with final primitives like `int` strangely

实际上,Gson 确实
工作正常。我忘记了Java常量内联。使用获得count通孔反射Field效果完美。但是,由于内联,返回了contant值。与相似的普通对象javap -p -c

final class ext.Test$Immutable {
  private final int foo;

  private ext.Test$Immutable();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_0
       6: putfield      #2                  // Field foo:I
       9: return

  private int getFoo();
    Code:
       0: iconst_0
       1: ireturn

  public java.lang.String toString();
    Code:
       0: ldc           #4                  // String (IMMUTABLE:0)
       2: areturn
}

在这种情况下,甚至toString()返回一个常数。是的,这就是Java和javac工作方式。为了禁用这种内联并将final修饰符添加到该字段,就像周围的所有字段一样,应添加一个非编译时值:

    @SerializedName("count")
    @Expose
    private final int count = constOf(0);

哪里constOf(int)仅仅是:

private static int constOf(final int value) {
    return value;
}

现在,所有传入的DTO字段都可以轻松声明final

2020-11-30