当前位置: 首页 > 知识库问答 >
问题:

使用JAXB解组嵌套集

姬俊远
2023-03-14

抱歉长帖,提前感谢!

<RootTag>

  <Container type="type1">
    <value name="name1"/>
    <value name="name2"/>
  </Container>

  <Container type="type2">
    <value name="name3"/>
    <value name="name4"/>
  </Container>

</RootTag>

在我的JAXB中(代码如下),我有一组值集,其中内部集被包装成一个容器类。所以,我有一组值容器,其中一个值是一个泛型类。问题:除非选择的泛型类是硬编码的,否则值不会得到解析。

关于XML的说明:

>

  • 只应该有两

    包装所有这些,我们有一个包含Set的Root类

    java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to ElementName
    

    在Container类中,我指定了标签名称

    @XmlElement(name = "value")
    

    如果我只是在这里对类型进行硬编码,如下所示:

    @XmlElement(name = "value", type = ElementName.class)
    

    然后JAXB正确解析标记。但是,正如我所说,这个类应该是通用的,所以这对我来说不是一个真正的解决方案。

    import javax.xml.bind.Unmarshaller;
    import javax.xml.bind.annotation.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /*
     * <RootTag>
     *     *
     * </RootTag>
     */
    @XmlRootElement(name = "RootTag")
    public class Root {
    
        /* <Container type = "type1">
         *   <value name = "name1"/>
         *   <value name = "name2"/>
         * </Container>
         * <Container type = "type2">
         *   <value name = "name3"/>
         *   <value name = "name4"/>
         * </Container>
         */
        @XmlElement(name = "Container")
        private Set<Container<ElementName>> containers;
    
        // for each of the two Container types 
        // we create a set of its corresponding values' names
        // and then we null the containers field
        @XmlTransient
        private Map<String, Set<String>> valuesNames = new HashMap<>(2);
    
        //Basically that is @PostConstruct, but works with JAXB
        void afterUnmarshal(Unmarshaller u, Object parent) {
    
            containers.forEach(container -> valuesNames.put(container.getType(),
                                                            container.getValues().stream()
                                                                     .map(ElementName::getName)
                                                                     .collect(Collectors.toSet())));
            containers = null;
        }
    
        /** return unique value names from both container types */
        public Set<String> getAllValuesNames() {
    
            return valuesNames.values().stream()
                              .flatMap(Set::stream)
                              .collect(Collectors.toSet());
        }
    }
    

    容器

    import javax.xml.bind.Unmarshaller;
    import javax.xml.bind.annotation.*;
    import java.util.Set;
    
    /*
     * <* type = "type1">
     *   <value *>*
     *   <value *>*
     * </*>
     */
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public  class Container<T> {
    
        @XmlAttribute
        private String type;
    
        @XmlElement(name = "value")
        private Set<T> values;
    
        public String getType() {
    
            return type;
        }
    
        public Set<T> getValues() {
    
            return values;
        }
    }
    

    元素名称

    import javax.xml.bind.Unmarshaller;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    
    /*
     *  <* name = "name2"/>
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    public class ElementName {
    
        @XmlAttribute
        private String name;
    
        public String getName() {
    
            return name;
        }
    }
    
    public static void main(String[] args) throws JAXBException {
    
        final String xmlSerialized = "<RootTag>\n" +
                                     "  \n" +
                                     "  <Container type=\"type1\">\n" +
                                     "    <value name=\"name1\"/>\n" +
                                     "    <value name=\"name2\"/>\n" +
                                     "  </Container>\n" +
                                     "  \n" +
                                     "  <Container type=\"type2\">\n" +
                                     "    <value name=\"name3\"/>\n" +
                                     "    <value name=\"name4\"/>\n" +
                                     "  </Container>\n" +
                                     "  \n" +
                                     "</RootTag>";
    
        System.out.println("XML:\n" +
                           "=======================================\n" +
                           xmlSerialized +
                           "\n=======================================");
    
        //works just the same with only Root.class
        JAXBContext  context      = JAXBContext.newInstance(Root.class, Container.class, ElementName.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
    
        Root xmlDeserialized = (Root) unmarshaller.unmarshal(new StringReader(xmlSerialized));
        System.out.println("Values' names: " + xmlDeserialized.getAllMetricNames());
    }
    

    如果一切正常(如果我在Container类的注释中硬编码ElementName.class),它会给出以下输出:

    XML:
    =======================================
    <RootTag>
      <Body>
        <Container type="type1">
          <value name="name1"/>
          <value name="name2"/>
        </Container>
        <Container type="type2">
          <value name="name3"/>
          <value name="name4"/>
        </Container>
      </Body>
    </RootTag>
    =======================================
    Values' names: [name4, name3, name2, name1]
    

    用JAXB解组通用列表。

    >

  • 主要思想是代替使用List

    之前:

    @XmlElement(name = "value")
    private Set<T> values;
    

    之后:

    @XmlAnyElement(lax = true)
    private Set<T> values;
    

    再次运行(JAXBContext.newInstance(Root.class,Container.class,ElementName.class)):没有任何变化,值仍然是ElementNSImpl类型。此外,我不喜欢我允许标记的任何名称,它必须是“值”。

    间歇性类广播在取消编组期间从元素NS到自己的类型

    在他们的案例中,问题是他们创建了JAXBContext而没有提供一些所需的类。不适用于我。

    JAXB解组XML类转换异常

    在他们的例子中,问题是Java类型擦除,这意味着Java在编译时确定泛型类型,然后编译代码,然后从泛型中删除类型,所以JVM不知道这些泛型类型是什么。我敢打赌这是我的情况,但我不知道如何检查或修复它。


  • 共有2个答案

    齐威
    2023-03-14
    匿名用户

    我找到解决办法了!我使用了Blaise Doughan在这里描述的方法,这是< code>XmlAdapter类的用法。我将一组值转换为一组字符串,将< code>ElementName改编为< code>String。然后是要映射的容器。

    附言。我像在我的另一个答案中一样保留了ContainerOfElementName。这是重复的代码,但似乎没有更好的方法来创建JAXB层次结构。

    p、 p.s.问题是Java类型擦除(IMHO)<代码>设置

    孙阳旭
    2023-03-14

    当前解决方法-非常欢迎其他答案

    我在对泛型类型进行硬编码时利用了一点这个想法——我将Container类拆分为两个类和

    容器

    import javax.xml.bind.annotation.*;
    import java.util.Set;
    
    /*
     * <Container type = "type1">
     *   *
     * </Container>
     */
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public abstract  class Container {
    
        @XmlAttribute
        private String type;
    
        public String getType() {
    
            return type;
        }
    
        /** Derived classes will declare the generic type for us. */
        public abstract Set<?> getValues();
    }
    

    元素名称的容器

    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /*
     * <Container type = "type1">
     *   <value name="name1"/>
     *   <value name="name2"/>
     * </Container>
     */
    public class ContainerOfElementName extends Container {
    
        @XmlElement(name = "value")
        private Set<ElementName> values;
    
        @Override
        public Set<ElementName> getValues() {
    
            return values;
        }
    
        public Set<String> getNames() {
            return values.stream().map(v -> v.name).collect(Collectors.toSet());
        }
    
        /*
         *  <* name = "name2"/>
         */
        @XmlAccessorType(XmlAccessType.FIELD)
        public static class ElementName {
    
            @XmlAttribute
            private String name;
        }
    }
    

    现在在Root类中:

    @XmlElement(name = "Container")
    private Set<ContainerOfElementName> containers;
    
    void afterUnmarshal(Unmarshaller u, Object parent) {
    
        containers.forEach(container -> valuesNames.put(container.getType(),
                                                        container.getNames()));
        containers = null;
    }
    

    这有效,但它迫使我为每个可能的类创建容器的子类,该类表示

     类似资料:
    • 问题内容: 我如何指示JAXB处理此问题? XML格式 根java 这不起作用… allChildren为空。 问题答案: 您可以更改模型并执行以下操作: 根 父母 更新 有可能避免上课吗? 有两种不同的方法可以完成此操作: 选项#1-使用XmlAdapter的任何JAXB实现 您可以使用XmlAdapter虚拟添加类。 子适配器 根 该注释被用于引用。 儿童 选项#2-使用EclipseLink

    • 我目前正在学习如何在android中使用Jaxb解析xml文件。但是我不知道代码中有什么错误,以及在哪里和如何纠正它。我无法解析xml并获得食品列表。如果我删除List并简单地把它写成Food,那么只有xml中的最后一个元素被解析,其余的似乎都被覆盖了。请帮助我。 我试图解析http://www.w3schools.com/xml/simple.xml,,目前我有这样的代码: ---- 用于取消

    • 问题内容: 我正在尝试创建一个非常简单的REST服务器。我只是有一个测试方法,它将返回字符串列表。这是代码: 它给出以下错误: 我希望JAXB对诸如String,Integer等简单类型具有默认设置。我想不是。这是我的想象: 使这种方法最简单的方法是什么? 问题答案: 我使用@LiorH的示例并将其扩展为: 注意,它使用泛型,因此您可以将其与String之外的其他类一起使用。现在,应用程序代码很简

    • 如有任何线索将不胜感激。谢谢,约翰 XML是巨大的,但这里有一个片段,序列中的媒体是空的,而它不应该是空的。

    • 我试图让Maven使用嵌套的xml模式生成JAXB绑定:模式a导入模式B。模式B导入模式C。 如果模式A中的对象引用了模式B中不依赖于模式C对象的对象,那么一切都构建得很好。如果模式B中的对象引用了模式C中的对象,它就会中断。也就是说,深入一层是可行的。去两个深度没有。 只要我在模式A中添加一个引用模式B中对象的对象,而模式B又引用了模式C中的对象,我就会得到一个组织。xml。萨克斯。SAXPar

    • 与marshall和unmarshall的读者和作者有争议。就是这样。这就是我如何向版画作者写东西的方式。 我是这样得到它的: 然后此解组操作将挂起。它没有给我任何东西,甚至没有例外。