V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
DavidDee
V2EX  ›  Java

JAXB 怎么实现带泛型 XML 转 Bean。

  •  
  •   DavidDee · 2021-08-16 16:14:31 +08:00 · 1532 次点击
    这是一个创建于 1197 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 转换的 xml 格式*(其中 DataTable 节点下 a 、b 字段是 X 类,c 、d 字段是 Y 类的,后续还需要适配更多类)
    <?xml version="1.0" encoding="GB2312"?>
    <DocumentElement>
      <Result>0</Result>
      <Error></Error>
      <DataTable>
          <a>/<a>
          <b>/<b>
      </DataTable>
      <DataTable>
          <a>/<a>
          <b>/<b>
      </DataTable>
    </DocumentElement>
    <!--------------------------或者-------------------------->
    <?xml version="1.0" encoding="GB2312"?>
    <DocumentElement>
      <Result>0</Result>
      <Error></Error>
      <DataTable>
          <c>/<c>
          <d>/<d>
      </DataTable>
      <DataTable>
          <c>/<c>
          <d>/<d>
      </DataTable>
    </DocumentElement>
    
    • 目的 bean
    @XmlRootElement(name = "DocumentElement")
    @XmlSeeAlso({X.class, Y.class})
    @XmlAccessorType(XmlAccessType.FIELD)
    public class ResponseEntity <T> {
    
        @XmlElement(name = "Result")
        private String result;
        @XmlElement(name = "Error")
        private String error;
        @XmlAnyElement(lax = true)
        private List<T> dataTable;
    
        public ResponseEntity() {
    
        }
    
       // getter/setter 省略
    }
    
    • X 类
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class X {
        private String a;
        private String b;
    }
    
    • Y 类
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Y {
        private String c;
        private String d;
    }
    
    • 转换方法
            //获得 JAXBContext 类的新实例。参数为类的地址
            JAXBContext context = JAXBContext.newInstance(t.getClass());
            //创建一个可以用来将 XML 数据转换为 java 内容树的 Unmarshaller 对象。
            Unmarshaller um = context.createUnmarshaller();
            //创建一个 StringReader 将 xml 报文转成流
            StringReader sr = new StringReader(xml);
            //调用 unmarshal 进行转换,并把 Object 类型强转为调用者的类型
            t = (T) um.unmarshal(sr);
            //将对象返回给调用者
            return t;
    

    目前的问题

    • 如果 X 、Y 类 @XmlRootElement 不加 name = "DataTable",转换后的 Bean 的 list 为空
    • 如果 X 、Y 类 @XmlRootElement 其中一个类加 name = "DataTable",转换后的 Bean 的 list 类型内容一直都是那个类
    • 如果 X 、Y 类 @XmlRootElement 都加 name = "DataTable",转换后的 Bean 的 list 类型会不匹配(猜测 java 泛型在编译时类型被擦去, 反射不能确定具体那个类,参考[当 Jaxb 遇到泛型]: https://www.cnblogs.com/mumuxinfei/p/8948299.html)

    不知道各位有没有遇到过或提供一下解决思路。不胜感激

    5 条回复    2021-08-31 15:16:53 +08:00
    DavidDee
        1
    DavidDee  
    OP
       2021-08-16 17:29:06 +08:00
    算了,都用 DataTable 节点的话不行,我手动这个这点内容吧,然后再使用包装类用不同的 name 解析,不浪费时间了
    cp19890714
        2
    cp19890714  
       2021-08-16 22:20:01 +08:00   ❤️ 1
    这不是类型擦除导致的, 而是因为你就没有传入具体类型, 程序自然不知道你要反序列化为什么玩意.
    jackson 在反序列化 json 时, 需要传入目标类型的 TypeReference, 其中需要指明泛型的具体类型. 如果没有指定, 同样不能反序列化.
    jaxb 也是一样.

    即使能实现, 那也是通过遍历,或者你指定的一些规则, 去猜测这个泛型可能的具体类型.
    hdfg159
        3
    hdfg159  
       2021-08-17 08:01:49 +08:00 via Android   ❤️ 1
    用数组
    DavidDee
        4
    DavidDee  
    OP
       2021-08-17 10:52:30 +08:00
    @cp19890714 感谢回复。其实在之前已经使用过包装类,将具体需要转换的类型放入泛型了,但是还是有问题。
    8bit
        5
    8bit  
       2021-08-31 15:16:53 +08:00
    用继承的方式解决,DataTable 作为父类,各子类使用自己类名作为 XML 节点名,在转换完之后将该节点名替换为 DataTable,这就解决了泛型未传入类型的问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1583 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:00 · PVG 01:00 · LAX 09:00 · JFK 12:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.