Как я могу изменить поток вывода JAXB, чтобы включить произвольный встроенный XML? - PullRequest
5 голосов
/ 19 ноября 2010

Я хотел бы изменить выходной поток для операции маршалинга 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";

}

, который обычно используется для этого:

<Product>
  <CommonProperty>Something</CommonProperty>
  <ExtraXml>Something else</ExtraXml>
</Product>

А что, если в поле extraXml содержится какой-то дополнительный XML (произвольной сложности), который должен быть включен в строку с окончательным маршализованным результатом?

Скажем, extraXml Содержит "<abc><def>Something extra</def></abc> ", мне бы очень хотелось решение, которое позволило бы мне маршалировать Product следующим образом (необязательное форматирование):

<Product>
  <CommonProperty>Something</CommonProperty>
  <abc>
    <def>Something extra</def>
  </abc>
</Product>

Я смотрел на этот связанный вопрос , ноэто не совсем дало желаемый результат, так как кажется, что оно больше ориентировано на общее изменение формата, чем на вставку DOM.

Свойство extraXml только для иллюстрации, оно может быть помечено как @XmlTransient или в отдельный специализированный класс.Единственным критерием является то, что он может каким-то образом получить String, содержащий совершенно произвольный XML-контент для добавления к отдельному Product маршаллированному выводу.

Я также должен упомянуть, что потребители вывода этого могутразбирать произвольный контент так, как им удобно.Цель здесь - упростить обработку на стороне сервера.

Заранее благодарим за любую помощь, которую вы можете предложить.

Ответы [ 2 ]

6 голосов
/ 19 ноября 2010

Вы можете представить дополнительный 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;

}

Затем, когда вы умаршали XML-документ, такой как:

<Product>
  <CommonProperty>Something</CommonProperty>
  <abc>
    <def>Something extra</def>
  </abc>
</Product>

С помощью следующего кода:

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);
    }
}

«Дополнительный XML» будет сохраненкак узел DOM.

5 голосов
/ 19 ноября 2010

С помощью Блейза Дафана - вот окончательное решение, на котором я остановился. Это включает в себя дополнительный демонстрационный код, чтобы помочь другим, кто может оказаться в этой ситуации.

Занятия

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>();

}

Продукт (с модификациями Блейза)

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;

}

Main (некоторый демонстрационный код)

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);

  }
}

Окончательный вывод:

<?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>

Результат!

Но в 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.

сообщается об исключении. Это (вероятно) связано с тем, что xercesImpl.jar в папках JBoss lib и /lib/endorsed используют функцию переопределения Java META-INF/Services, чтобы предотвратить сортировку JDK с использованием внутренних классов. Возможно, вам придется указать альтернативу DocumentBuilderFactory напрямую, используя следующий подход:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance("oracle.xml.jaxp.JXDocumentBuilderFactory", this
      .getClass().getClassLoader());

Реализация Oracle, кажется, облегчает эти проблемы, возможно, потому, что поддерживает осведомленность о классах узла DOM в контексте JAXB. Эти классы находятся в xdb.jar и xmlparserv2.jar, поставляемых с клиентом Oracle.

Надеюсь, это поможет.

...