部分 GSON 反序列化

2024-04-19

我正在实施一个自定义JsonDeserializer因为处理所需的业务逻辑。但有些部分可以用标准方式解析。这是否可能 - 自己处理一些元素并让一些嵌套元素自动处理?

这是 JSON:

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

这是自定义解串器definition item:

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枚举。我可以自己做,这只是几行代码,但我更喜欢库尽可能多地做。


...但我更希望图书馆尽其所能。

好吧,就用Gson吧。

有的是数据传输对象 https://en.wikipedia.org/wiki/Data_transfer_object模式,特别是 Gson 映射类,可以完美地解决您的问题。默认情况下,如果 Gson 能够通过内置设施满足映射,那么除特殊情况外,您不必自己完成它的工作。此类映射类仅存在于 JSON 内容和您的业务对象类之间,以便对数据进行(反)序列化(简单来说,DTO 仅出于此目的而存在,并且与 Gson 相关的注释不得传播到您的业务类上 -只需将 DTO 转换为业务对象)。

Mappings

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);
        }
    }

}

Demo

现在你可以使用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
    }
}

Output:

包装器{id='10',游戏=[PZ],formulaDefinition=FormulaDefinition{count=10,operatorDefinitions=[OperatorDefinition{operators=PLUS,第一个='1-5',第二个='1-5',结果=' 2-5'}]}}


Edit

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

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

其实是Gsondoes工作正常。我忘记了 Java 常量内联。得到count通过反射使用Field工作完美。但是,由于内联,会返回常量值。类似的普通对象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);

where constOf(int)只是:

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

部分 GSON 反序列化 的相关文章

  • SpringBoot @SqsListener - 不工作 - 有异常 - TaskRejectedException

    我有一个 AWS SQS 队列中已有 5000 条消息 示例消息类似于 Hello 1 我创建了一个 SpringBoot 应用程序 并在其中一个组件类中创建了一个从 SQS 读取消息的方法 package com example aws
  • 使用罗马图书馆获取所有 RSS 提要条目

    我正在使用 Java 的 Rome 库来解析一些 RSS 默认情况下需要 25 个条目 请告诉我 如何获得接下来的 25 个条目 我的测试代码是 public static SyndFeed getSyndFeedForUrl String
  • 在 JSF/JSP EL 和 Javascript 中连接字符串[重复]

    这个问题在这里已经有答案了 我在使用 EL 和 javascript 函数 JSF 1 2 Facelets Richfaces 3 3 0GA 时遇到问题 我有一个页面包含另一个组合
  • 超时后如何重新建立 JDBC 连接?

    我有一个长时间运行的方法 它通过 EntityManager TopLink Essentials 执行大量本机 SQL 查询 每个查询只需要几毫秒即可运行 但查询数量却有数千个 这发生在单个 EJB 事务内 15 分钟后 数据库关闭连接
  • Eclipse 插件:应有的自动完成功能

    我有一个问题 有多种可能的解决方案 我正在学习计算机科学 目前正在担任实习生 我的任务是为 Android 和 iOS 制作一个商业应用程序 我现在已经使用 Visual studio 2010 工作了 2 年 Xcode 相当相似 所以这
  • HSQLDB - 这是主数据库文件

    我在嵌入模式下使用 HSQLDB jdbc hsqldb file abc TESTDB 创建数据库后 文件夹abc有以下文件 TESTDB lck TESTDB script TESTDB log TESTDB properties 我的
  • 如何将 AES CCM 与 Bouncycastle JCE 提供程序一起使用 - CCMParameters

    是否可以使用JCE来执行CCM 我在互联网上看到很多使用非 JCE bouncycastle 类的示例 特别是 我看到他们调用 init 并传入 CCMParameters 对象 问题是 这个 CCMParameters 对象不是从 Alg
  • Hibernate 验证器:违规消息语言

    我有一个测试类 我正在测试一个域模型 该模型用例如注释 NotNull 在我的测试课中 我首先得到验证器 private static Validator validator BeforeClass public static void s
  • 如何设置按钮的大小?

    我将按钮放在带有 GridLayout 的 JPane 中 然后我用 BoxLayout Y AXIS 将 JPanel 放入另一个 JPanel 中 我希望 GridLayout 中的按钮是方形的 我使用 tmp setSize 30 3
  • 如何从github项目获取jar? [复制]

    这个问题在这里已经有答案了 我想使用官方网站上的 kSoap2 android 库http simpligility github io ksoap2 android index html http simpligility github
  • 从已排序的 ArrayList 中删除重复项,同时保留重复项中的某些元素

    好吧 一开始我以为这会很简单 但我想不出有效的方法来解决这个问题 我想出了一种蛮力的方法来解决这个问题 但这不是很优雅 我有一个数组列表 Contacts 是一个 VO 类 有多个成员 名称 区域 id ArrayList中存在重复项 因为
  • EJB3 - 通过注入与查找获取 bean - 有什么区别、影响和陷阱?

    我知道有两种获取 EJB 实例的方法 通过 EJB 注释在 servlet 和 EJB 中进行依赖注入 在任何地方通过 Context lookup 进行 JNDI 查找 使用这两种方法有哪些差异 影响和陷阱 它们是一样的吗 依赖注入比查找
  • 如何动态更新属性文件?

    我的应用程序是一个批处理过程 它从 application properties 文件中提取环境变量 我使用的密码必须每隔几个月更新一次 我想自动化密码更新过程并将更新后的密码保存在属性文件中 以便在将来的运行中使用 但我尝试进行的任何更新
  • CoreNLP 如何识别小写的命名实体,例如 kobe bryant?

    我遇到一个问题 CoreNLP 只能识别以大写字符开头的命名实体 例如科比 布莱恩特 Kobe Bryant 但无法识别科比 布莱恩特 kobe bryant 作为一个人 那么CoreNLP如何识别以小写字符开头的命名实体 赞赏它 首先 您
  • Java 到 ruby​​ AES/ECB/PKCS5Padding 加密

    我有一个使用第三方支付门户的在线电子商务网站 支付门户一直运行良好 直到第三方支付门户要求每个人开始使用带有其他支付参数的哈希密钥 现在的问题是第三方支付门户只提供了一页文档来实现哈希密钥 这是提供的文档 加密演算法 为了减少数据传输和发布
  • Fragment中有类似setResult()的方法吗?

    我正在使用一个片段 我收到错误onResult 方法 我需要一个替代方法setResult RESULT OK data 我可以在我的片段中使用它 请帮忙 日历片段 package app pal study samplestudy imp
  • 无法查找 Websphere 8.5 中是否启用了 SSL

    我编写了一个简单的 ejb 瘦客户端来查找 IBM WebSphere 8 5 中部署的 bean 当服务器上未启用 SSL 时 我能够成功查找 bean 但是一旦启用 SSL 我就开始收到下面提到的异常 This is how I ena
  • 通过 awselb 使用 ssl 时的 neo4j java 驱动程序问题

    I am using neo4j community version 3 1 1 and enterprise edition version 3 0 1 with ssl configured through awselb To conn
  • Java Webstart 和 URLConnection 缓存 API

    的描述URLConnection 缓存 API http docs oracle com javase 6 docs technotes guides net http cache html最后一句指出 Java 2 标准版中没有 URLC
  • 总小时数无法从 Android 插入 MySQL

    我使用以下公式获得总小时数 public void updateTotalHours int a SplitTime objMyCustomBaseAdapter getFistTime int b SplitTime objMyCusto

随机推荐