Jaxb: как размонтировать xs: любую часть XML-строки?
У меня есть приложение, делающее конверсии XML ↔ , используя Jaxb и автоматически создаваемые классы с maven-jaxb2-plugin.
Где-то глубоко в моей схеме, у меня есть возможность ввести "ЛЮБОЙ" xml.
Обновление: это лучше описывает мою схему. Некоторые известные XML-обертывания полностью неизвестной части ( "любая" часть).
<xs:complexType name="MessageType">
<xs:sequence>
<xs:element name="XmlAnyPayload" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:any namespace="##any"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OtherElements">
....
</xs:sequence>
Это отображает (по jaxb) во внутренний класс, подобный этому.
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"any"
})
public static class XmlAnyPayload {
@XmlAnyElement(lax = true)
protected Object any;
Когда я разобрал всю структуру, это не проблема. "Объект любой" отобразит в org.apache.xerces.dom.ElementNSImpl. Теперь я хочу восстановить объект Java вручную, а затем перейти к XML. Как взять некоторый случайный XML и поместить в какой-либо элемент (org.apache.xerces.dom.ElementNSImpl), чтобы создать объект Java?
Кроме того, следующий случай, когда у меня есть этот элемент как java, я хочу размонтировать эту самую часть (чтобы извлечь XML-строку этого элемента). Но это невозможно. Я получаю исключение из корневых элементов. Но невозможно аннотировать ElementNSImpl.
unable to marshal type "com.sun.org.apache.xerces.internal.dom.ElementNSImpl" as an element because it is missing an @XmlRootElement annotation
Есть ли у вас какие-либо предложения по устранению этих проблем?
Ответы
Ответ 1
@XmlAnyElement(lax = true)
означает на простом английском языке что-то вроде:
Дорогой JAXB! Если у вас есть сопоставление для этого элемента, пожалуйста, отмените его в объект Java. Если вы не знаете этот элемент, просто оставьте его как Элемент DOM.
Это именно то, что происходит в вашем случае. Поэтому, если вы хотите фактически развязать содержимое этого слабого, укажите JAXB-контекст с отображением для элемента, который вы хотите развязать. Самый простой способ сделать это - аннотировать ваш класс с помощью @XmlRootElement
@XmlRootElement(name="foo", namespace="urn:bar")
public class MyClass { ... }
Теперь, когда вы создаете свой JAXB-контекст, добавьте в него MyClass
:
JAXBContext context = JAXBContext.newInstance(A.class, B.class, ..., MyClass.class);
В этом случае, если JAXB встречает элемент {urn:bar}foo
вместо этого xs:any
, он будет знать, что этот элемент отображается на MyClass
и попытается отключить MyClass.
Если вы создаете контекст JAXB на основе имени пакета (вероятно, вы это делаете), вы можете добавить к нему класс (скажем, com.acme.foo.MyClass
). Самый простой способ - создать ресурс com/acme/foo/jaxb.index
:
com.acme.foo.MyClass
И добавьте имя пакета в контекстный путь:
JAXBContext context = JAXBContext.newInstance("org.dar.gee.schema:com.acme.foo");
Существуют и другие способы с ObjectFactory
и т.д., но трюк с jaxb.index
, вероятно, самый простой.
В качестве альтернативы вместо разметки всего за один проход вы можете оставить содержимое xs:any
как DOM и развязать его в целевой объект во втором unmarshalling с anothe JAXB-контекстом (который знает ваш класс MyClass
). Что-то вроде:
JAXBContext payloadContext = JAXBContext.newInstance(MyClass.class);
payloadContext.createUnmarshaller().unmarshal((Node) myPayload.getAny());
Этот подход иногда лучше, особенно если у вас есть комбинация схем контейнера/полезной нагрузки, которые относительно независимы. Зависит от случая.
Все сказанное выше относится также к сортировке. Все это аккуратно двунаправлено.
Ответ 2
Я думаю, вам нужны XSD для этой "любой" части и генерировать классы для них.
Вот еще информация:
http://jaxb.java.net/guide/Mapping_of__xs_any___.html
Изменить: если ваш объект, который вы хотите маршалировать, не имеет аннотации @XmlRootElement (см. сообщение об ошибке), то я думаю, вам нужно обернуть его с помощью JAXBElement.
Ответ 3
<xs:any/>
требуется некоторая неинтуитивная информация для преобразования в объект Java. Если у вас нет разницы, попробуйте использовать
<element name="any" type="xs:anyType"/>