Jaxb:如何在解组时替换给定对象树中的类/绑定

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jaxb:如何在解组时替换给定对象树中的类/绑定相关的知识,希望对你有一定的参考价值。

我有一组给定的类来将xml解组为对象树。现在我得到一个扩展的xml,并希望用扩展版本替换对象树中的一个类。

XML文件

<?xml version="1.0" encoding="UTF-8"?>
<RootNode_001>
    <systemId>on the 4</systemId>
    <systemName>give me some more</systemName>
    <person>
        <firstname>James</firstname>
        <lastname>Brown</lastname>
        <address>
            <street>Funky</street>
            <city>Town</city>
            <type>HOME</type> <!-- this is the new field -->
        </address>
    </person>
</RootNode_001>

我使用新字段创建一个新的地址类,如下所示:

public class ExtAddress extends Address {
    // inherit from address and add new field
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

现在我尝试解组成一个对象树,我希望ExtAddress成为树的一部分,如下所示:

public class Runner {

    public static void main ( String argv[] ) throws Exception {
        File file = new File( "basic.xml" );
        Class cls = RootNode.class;

        // create a context with the root node and the replacement class
        JAXBContext jaxbContext = JAXBContext.newInstance( ExtAddress.class, cls );
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        JAXBElement jaxbElement = unmarshaller.unmarshal( new StreamSource( file ), cls );
        RootNode rootNode = (RootNode) jaxbElement.getValue();

        System.out.println( rootNode.getClass().getName() );
        System.out.println( rootNode.getPerson().getClass().getName()  );
        // this returns Address but I want ExtAddress
        System.out.println( rootNode.getPerson().getAddress().getClass().getName()  );
    }
}

到目前为止我没有使用注释。 unmarshalled对象树返回Address而不是ExtAddress。如果我添加一个XmlType注释,我会得到一个例外:

@XmlTyp( name = "address" )
ExtAddress extends Address {
  ...
}

Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "address". Use @XmlType.name and @XmlType.namespace to assign different names to them.
    this problem is related to the following location:
        at jaxb.standard.Address
        at jaxb.ExtAddress

我尝试了很多东西,但这似乎非常接近解决方案。如何告诉jaxb使用继承的类而不是原始的类。

我想在库中提供一组标准类,并且当xml中的新字段出现时,能够稍后将对象树更改为扩展。

答案

使用extAddress而不是地址更改xml将起到作用。

另一答案

你应该做XML模式版本控制而不是这个,但是difficult做得很好。链接文章中有一个pdf,你应该阅读它。

我在过去做过“选项3更改架构的targetNamespace”,这样就可以执行以下操作:

每个版本都有不同的targetNamespaces:

targetNamespace="http://www.exampleSchema.com/v1.0"
targetNamespace="http://www.exampleSchema.com/v1.1"
...

您可以为不同组中的每个命名空间生成必要的JAXB类:

com.example.schema.generated.v10
com.example.schema.generated.v11
...

从这里开始,您可以尝试阅读它:

  • 你可以签订合同,文件必须以模式的版本结尾:file1_v10.xml,file2_v11.xml
  • 您可以尝试使用最新的JAXB类读取,如果失败,则先前等等
  • 你可以在第一个n。 line作为文件的文本,并解析targetNamespace,并应用正确的JAXB
  • 或者你有另一个option等...

(当然,你必须在解组后单独处理每个版本,这不一定是坏事)

要么

您可以决定只使用1个模式,并且只能使用可选(minOccurs =“0”)元素扩展模式,它很难看,并且有一些限制,但有时它就足够了。

要么

或者,您可以以不同的方式执行此操作:您可以使用另一个支持模式版本控制的XML框架,例如:JMRIJiBX(它们不是当前或主动开发的,只是想展示一些示例。我真的想链接是另一个,但不幸的是我忘了它的名字)。

另一答案

这里讨论类似的主题:JAXB inheritance, unmarshal to subclass of marshaled class

有了jackson 2.8.6你就可以写出像

@Test
public void readXmlToSelfDefinedPojo2() throws Exception {

    ObjectMapper mapper = new XmlMapper();
    ExtAdress pojo = mapper.readValue(
                    Thread.currentThread().getContextClassLoader().getResourceAsStream("52109685.xml"),
                    ExtAdress.class);
    System.out.println(toString(pojo));
}

public String toString(Object obj) throws JsonGenerationException, JsonMappingException, IOException{
    StringWriter w = new StringWriter();
    new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true).writeValue(w, obj);
    return w.toString();
}

它会打印出来

{
  "systemId" : "on the 4",
  "type" : "test"
}

对于

<?xml version="1.0" encoding="UTF-8"?>
  <RootNode_001>
<systemId>on the 4</systemId>
<systemName>give me some more</systemName>
<type>test</type>
<person>
    <firstname>James</firstname>
    <lastname>Brown</lastname>
    <address>
        <street>Funky</street>
        <city>Town</city>
        <type>HOME</type> <!-- this is the new field -->
    </address>
</person>
</RootNode_001>

@JsonIgnoreProperties(ignoreUnknown = true)
public class ExtAdress extends Adress {
    public String type;
    public ExtAdress() {
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
public class Adress {
    public String systemId;

    public Adress() {
    }
}

以上是关于Jaxb:如何在解组时替换给定对象树中的类/绑定的主要内容,如果未能解决你的问题,请参考以下文章

如何调试 JAXB 解组?

YouTube Android Player API 在解组时抛出“BadParcelableException ClassNotFoundException:asc”与新的 YouTube 版本

JAXB使用XMLReader解组时的未读属性

使用 Jaxb API 将 XML 解组为 Java 对象时获取 NullPointerException

如何将 SOAP XML 解组为 Java 对象 [重复]

在解组xml文件时,Field属性将变为null