如何修改JAXB编组输出流以包含任意内联XML?

Gar*_*owe 5 java xml jaxb marshalling

我想修改JAXB编组操作的输出流以包含一些任意XML.这是一个澄清情况的例子.

我有一个Product带有JAXB注释的任意域对象,目前看起来像这样:

@XmlRootElement(name="Product")
public class Product {

  @XmlElement(name="CommonProperty")
  private String commonProperty="Something";  

  @XmlElement(name="ExtraXml")
  private String extraXml="Something extra";

}
Run Code Online (Sandbox Code Playgroud)

通常会编组进行此操作:

<Product>
  <CommonProperty>Something</CommonProperty>
  <ExtraXml>Something else</ExtraXml>
</Product>
Run Code Online (Sandbox Code Playgroud)

现在,如果该extraXml字段包含一些额外的XML(任意复杂性),并将其与最终的编组结果一起包含,该怎么办?

说,extraXml包含" <abc><def>Something extra</def></abc>",我真的想要一个能让我Product像这样编组的解决方案(格式化可选):

<Product>
  <CommonProperty>Something</CommonProperty>
  <abc>
    <def>Something extra</def>
  </abc>
</Product>
Run Code Online (Sandbox Code Playgroud)

我已经看过这个相关的问题,但它并没有完全产生我所追求的结果,因为它似乎更倾向于整体格式更改而不是DOM插入.

extraXml属性只是为了说明,它可以标记为@XmlTransient或单个专门的类.唯一的标准是它可以以某种方式获得String包含完全任意的XML内容以附加到单独的Product编组输出.

我还应该提到,由此产生的输出的消费者能够以适合他们的方式解析任意内容.这里的目的是简化服务器端的处理.

提前感谢您提供的任何帮助.

bdo*_*han 6

您可以将额外的XML表示为DOM节点而不是String.

将extraXML属性更改为org.w3c.dom.Node而不是String,并使用@XmlAnyElement对其进行批注.

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.w3c.dom.Node;

@XmlRootElement(name="Product")
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {

  @XmlElement(name="CommonProperty")
  private String commonProperty="Something";  

  @XmlAnyElement
  private Node extraXml;

}
Run Code Online (Sandbox Code Playgroud)

然后,当您解组XML文档时,例如:

<Product>
  <CommonProperty>Something</CommonProperty>
  <abc>
    <def>Something extra</def>
  </abc>
</Product>
Run Code Online (Sandbox Code Playgroud)

使用以下代码:

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Product.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("input.xml");
        Product product = (Product) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(product, System.out);
    }
}
Run Code Online (Sandbox Code Playgroud)

"额外XML"将保留为DOM节点.


Gar*_*owe 5

在这里,Blaise Doughan的帮助是我最终解决的问题.这包括一些额外的演示代码,以帮助可能发现自己处于这种情况的其他人.

课程

ProductList(一个新的包装类,用于显示多个Product条目的工作原理)

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

@XmlRootElement(name="ProductList")
public class ProductList {

  @XmlElementWrapper(name="Products")
  @XmlElement(name="Product")
  public List<Product> products = new ArrayList<Product>();

}
Run Code Online (Sandbox Code Playgroud)

产品(Blaise的修改)

import org.w3c.dom.Node;

import javax.xml.bind.annotation.*;

@XmlRootElement(name="Product")
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {

  @XmlElement(name="CommonProperty")
  public String commonProperty="Something";

  @XmlAnyElement
  public Node extraXml;

}
Run Code Online (Sandbox Code Playgroud)

主要(一些演示代码)

import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

public class Main {

  public static void main(String[] args) throws Exception {

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    // Build some arbitrary extra XML and prepare an InputStream
    String fragment1 = "<abc><def>Some extra 1</def></abc>";
    String fragment2 = "<ghi><jkl>Some extra 2</jkl></ghi>";
    Document document1 = factory.newDocumentBuilder().parse(new InputSource(new StringReader(fragment1)));
    Document document2 = factory.newDocumentBuilder().parse(new InputSource(new StringReader(fragment2)));

    Product product1 = new Product();
    product1.commonProperty = "Hello 1";
    product1.extraXml=document1.getFirstChild();

    Product product2 = new Product();
    product2.commonProperty = "Hello 2";
    product2.extraXml=document2.getFirstChild();

    ProductList productList = new ProductList();
    productList.products.add(product1);
    productList.products.add(product2);

    JAXBContext jc = JAXBContext.newInstance(ProductList.class, Product.class);
    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(productList, System.out);

  }
}
Run Code Online (Sandbox Code Playgroud)

最终输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProductList>
    <Products>
        <Product>
            <CommonProperty>Hello 1</CommonProperty>
            <abc>
                <def>Some extra 1</def>
            </abc>
        </Product>
        <Product>
            <CommonProperty>Hello 2</CommonProperty>
            <ghi>
                <jkl>Some extra 2</jkl>
            </ghi>
        </Product>
    </Products>
</ProductList>
Run Code Online (Sandbox Code Playgroud)

结果!

但它在JBoss中不起作用......

如果你在JBoss 4.2.3.GA或4.3中尝试上述内容,你可能会发现你得到了一个

class com.sun.org.apache.xerces.internal.dom.DocumentFragmentImpl nor any of its super class is known to this context.
Run Code Online (Sandbox Code Playgroud)

报告例外情况.这可能(可能)是由于xercesImpl.jarJBoss lib/lib/endorsed使用Java META-INF/Services覆盖工具的文件夹阻止JDK使用内部类进行编组.您可能需要DocumentBuilderFactory使用以下方法直接指定替代方法:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance("oracle.xml.jaxp.JXDocumentBuilderFactory", this
      .getClass().getClassLoader());
Run Code Online (Sandbox Code Playgroud)

Oracle实现似乎缓解了这些问题,可能是因为它在JAXB上下文中保持对DOM Node类的了解.这些类可以在Oracle客户端附带的xdb.jar和xmlparserv2.jar中找到.

希望能帮助到你.