Jackson 中的通用元组反序列化

2024-01-08

碰巧我需要支持来自外部数据源的 Java JSON 数据。有一种常见的模式。它是一个包含固定数量的某些不同类型元素的数组。我们称之为元组。下面是我使用 FasterXML Jackson 对具有特定预期元素类型的 3 元素元组进行反序列化的示例:

public class TupleTest {
    public static void main(String[] args) throws Exception {
        String person = "{\"name\":\"qqq\",\"age\":35,\"address\":\"nowhere\",\"phone\":\"(555)555-5555\",\"email\":\"[email protected] /cdn-cgi/l/email-protection\"}";
        String jsonText = "[[" + person + ",[" + person + "," + person + "],{\"index1\":" + person + ",\"index2\":" + person + "}]]";
        ObjectMapper om = new ObjectMapper().registerModule(new TupleModule());
        List<FixedTuple3> data = om.readValue(jsonText, new TypeReference<List<FixedTuple3>>() {});
        System.out.println("Deserialization result: " + data);
        System.out.println("Serialization result: " + om.writeValueAsString(data));
    }
}

class Person {
    public String name;
    public Integer age;
    public String address;
    public String phone;
    public String email;

    @Override
    public String toString() {
        return "Person{name=" + name + ", age=" + age + ", address=" + address
                + ", phone=" + phone + ", email=" + email + "}";
    }
}

class FixedTuple3 {
    public Person e1;
    public List<Person> e2;
    public Map<String, Person> e3;

    @Override
    public String toString() {
        return "Tuple[" + e1 + ", " + e2 + ", " + e3 + "]";
    }
}

class TupleModule extends SimpleModule {
    public TupleModule() {
        super(TupleModule.class.getSimpleName(), new Version(1, 0, 0, null, null, null));
        setSerializers(new SimpleSerializers() {
            @Override
            public JsonSerializer<?> findSerializer(SerializationConfig config,
                    JavaType type, BeanDescription beanDesc) {
                if (isTuple(type.getRawClass()))
                    return new TupleSerializer();
                return super.findSerializer(config, type, beanDesc);
            }
        });
        setDeserializers(new SimpleDeserializers() {
            @Override
            public JsonDeserializer<?> findBeanDeserializer(JavaType type,
                    DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException {
                Class<?> rawClass = type.getRawClass();
                if (isTuple(rawClass))
                    return new TupleDeserializer(rawClass);
                return super.findBeanDeserializer(type, config, beanDesc);
            }
        });
    }

    private boolean isTuple(Class<?> rawClass) {
        return rawClass.equals(FixedTuple3.class);
    }

    public static class TupleSerializer extends JsonSerializer<Object> {
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            try {
                jgen.writeStartArray();
                for (int i = 0; i < 3; i++) {
                    Field f = value.getClass().getField("e" + (i + 1));
                    Object res = f.get(value);
                    jgen.getCodec().writeValue(jgen, res);
                }
                jgen.writeEndArray();
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        }
    }   

    public static class TupleDeserializer extends JsonDeserializer<Object> {
        private Class<?> retClass;

        public TupleDeserializer(Class<?> retClass) {
            this.retClass = retClass;
        }

        public Object deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
            try {
                Object res = retClass.newInstance();
                if (!p.isExpectedStartArrayToken()) {
                    throw new JsonMappingException("Tuple array is expected but found " + p.getCurrentToken());
                }
                JsonToken t = p.nextToken();
                for (int i = 0; i < 3; i++) {
                    final Field f = res.getClass().getField("e" + (i + 1));
                    TypeReference<?> tr = new TypeReference<Object>() {
                        @Override
                        public Type getType() {
                            return f.getGenericType();
                        }
                    };
                    Object val = p.getCodec().readValue(p, tr);
                    f.set(res, val);
                }
                t = p.nextToken();
                if (t != JsonToken.END_ARRAY)
                    throw new IOException("Unexpected ending token in tuple deserializer: " + t.name());
                return res;
            } catch (IOException ex) {
                throw ex;
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        }
    }
}

但这种方法意味着每次我面对特定大小的元组中的新类型配置时,我都必须创建新类。所以我想知道是否有任何方法可以定义处理泛型类型的反序列化器。这样每个元组大小有一个元组类就足够了。例如,我的大小为 3 的通用元组可以定义如下:

class Tuple3 <T1, T2, T3> {
    public T1 e1;
    public T2 e2;
    public T3 e3;

    @Override
    public String toString() {
        return "Tuple[" + e1 + ", " + e2 + ", " + e3 + "]";
    }
}

它的用法如下:

List<Tuple3<Person, List<Person>, Map<String, Person>>> data = 
       om.readValue(jsonText, 
               new TypeReference<List<Tuple3<Person, List<Person>, Map<String, Person>>>>() {});

这是可行的还是不可行的?


好的。所以......可能有一种更简单的方法来实现“元组”风格。您实际上可以将 POJO 注释为序列化为数组:

@JsonFormat(shape=JsonFormat.Shape.ARRAY)
@JsonPropertyOrder({ "name", "age" }) // or use "alphabetic"
public class POJO {
   public String name;
   public int age;
}

如果是这样,它们将被写入数组,从数组中读取。

但是,如果您执行自定义泛型类型的操作,则可能需要解析类型参数。这可以使用以下方法完成TypeFactory, 方法findTypeParameters(...)。虽然这看起来似乎是多余的,但如果您进行子类型输入,那么对于一般情况来说这是必需的(如果没有,JavaType实际上有访问器direct类型参数)。

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

Jackson 中的通用元组反序列化 的相关文章

随机推荐

  • Magento - 数据未插入数据库,但id自动递增

    我正在为 Magento 开发一个新的支付模块 并且遇到了一个我无法解释的问题 验证信用卡后运行以下代码 table prefix Mage getConfig gt getTablePrefix tableName table prefi
  • cURL 错误 1:不支持的协议:https

    All 我尝试通过 PHP 的 cURL 包装器方法 HTTPS POST SOAP 请求 但不断收到以下 cURL 错误 错误 1 不支持的协议 https 有什么想法为什么会发生这种情况吗 目标 URL 很好 我可以通过命令行访问它 c
  • 如何从另一个 ViewController 修改 UITableView 的单元格

    在 VC 1 中 我有一个 UITableView 当我点击一个单元格时 我会进入 VC 2 其中显示有关该单元格的信息 我希望能够在 VC 2 中按下一个按钮 从而更改它在 VC 1 中对应的单元格的标题 但我对如何执行此操作感到困惑 我
  • 将请求参数作为 UTF-8 编码字符串传递[重复]

    这个问题在这里已经有答案了 我正在创建一个简单的登录页面 我想将登录名和密码参数作为 UTF 8 编码字符串传递 正如您在下面的代码中看到的 第一行是我将编码设置为 UTF 8 的地方 但这似乎毫无意义 因为它不起作用 当我使用带有重音符号
  • 交换活动后如何保持倒计时计时器计数?

    当我在活动之间交换时 我需要一个 countDownTimer 继续运行 我有多个活动 我将 countDownTimer 放在主活动中 但是当我交换到另一个活动并返回到主活动时 它又会从头开始重新计数 我相信是因为 countDownTi
  • 使用 pysftp 或 Paramiko,如何获得包含属性的完整目录列表?

    正如标题所示 我正在尝试获取目录中所有文件和目录的列表 包括它们的属性 我正在寻找至少名称 大小 上次修改时间 以及它是文件还是文件夹 我在 Windows 上使用 Python 3 我试过了listdir 我得到了一个没有属性的文件列表
  • java.lang.Long 和 scala.Long

    我不知道我的代码中发生了什么 日志在这里 error blahblah SampleApp scala 22 53 overloaded method value reduce with alternatives error func or
  • Oracle“总”计划成本确实低于其某些要素

    我不明白为什么有时一个计划的总成本可能很小 但查看计划内部我们会发现巨大的成本 确实查询速度很慢 有人能给我解释一下吗 这是一个例子 显然 成本较高的部分来自主选择中的一个字段 该字段在子视图上执行 listagg 并且该子视图的连接条件包
  • 在运行时创建XmlRpcUrl接口

    目前 我正在使用 xml rpc net 接口通过以下语句静态创建 XML RPC XmlRpcUrl http dillieodigital wordpress com xmlrpc php public interface ICSBlo
  • DLLImport 在哪里查找非托管 DLL?

    一个简单的问题 在 Net 中声明 DLLImport 属性时 运行时在哪里寻找解决该 DLL 依赖关系的方法 我是否必须转储 DLL 和所有itsbin 文件夹中的依赖项 我认为它的工作方式与 LoadLibrary 类似 Maybe 使
  • 如何比较 PHAsset 和 UIImage

    我已经转换了一些PHAsset to UIImage PHImageManager manager PHImageManager defaultManager manager requestImageForAsset asset targe
  • 隐藏页面中的div并使其仅在打印引导程序3 MVC 5上可见

    有一个网页向用户显示信息 如果用户决定打印它 我想包含屏幕上不需要的附加信息 但在打印时会很有帮助 为了实现这种行为 我试图做一个div仅在打印时可见 但它并没有起作用 div class visible print hidden lg h
  • 如何构建卡方分布表

    我想在 python 中生成一个卡方分布表作为概率水平和自由度的函数 给定已知的卡值和自由度 如何计算概率是这样的 In 44 scipy stats chisqprob 5 991 2 Out 44 0 05001161502657908
  • 在 git rebase -i 中删除提交不会减少 .git 文件夹的大小

    我有一个 git 存储库 其中 git文件夹是7MB 然后我添加并提交了一个 exe 文件 该文件是16MB其次是 git gc aggressive git prune 经过上述我的 git文件夹现在是23MB 接下来我做了一个git r
  • sklearn Kfold 访问单折叠而不是 for 循环

    使用 cross validation KFold n n folds folds 之后 我想访问索引以进行单折叠的训练和测试 而不是遍历所有折叠 那么我们来看一下示例代码 from sklearn import cross validat
  • 有没有一种方法可以在不使用暂存缓冲区的情况下更新纹理?

    我正在与https vulkan tutorial com https vulkan tutorial com 深度缓冲代码作为基础 进行了一些更改以每帧更新命令缓冲区 我正在使用一种粗略的方法来检查 fps 不确定它到底有多准确 但我正在
  • 亲子信号沟通陷入僵局

    我已经实现了两个程序 parent cpp child c 它们应该通过信号进行通信 以交替方式无限期地工作 然而 由于某种原因 程序在随机的时间后进入死锁 我无法弄清楚为什么 发送给父进程的信号似乎在没有执行信号处理程序的情况下被丢弃 因
  • git log --follow 已重命名的目录有什么技巧吗?

    git 命令有一个有用的命令可以在重命名后跟踪文件 如下所示git log follow path to some file 不幸的是 它仅适用于单个文件 我希望能够做相当于git log follow some directory 做到这
  • 是否可以在不使用 Javascript 的情况下在 Web 引擎中检索 HTML 元素?

    有没有办法识别点击元素的类型webView 如文本框 单选按钮 按钮等 使用webEngine getDocument 假设我单击一个文本字段 我可以获得单击位置的 x y 坐标 是否可以识别该特定位置中存在的元素 像这样的东西docume
  • Jackson 中的通用元组反序列化

    碰巧我需要支持来自外部数据源的 Java JSON 数据 有一种常见的模式 它是一个包含固定数量的某些不同类型元素的数组 我们称之为元组 下面是我使用 FasterXML Jackson 对具有特定预期元素类型的 3 元素元组进行反序列化的