gpt4 book ai didi

StAX 和命名空间

转载 作者:行者123 更新时间:2023-12-04 18:48:07 26 4
gpt4 key购买 nike

我正在尝试将一些代码从使用 DOM(通过 jDOM)转换为使用 StAX。同时,我正在从基于 DTD 的验证迁移到基于 XSD 的验证。哦,为了更好的衡量,我将 JAXB 引入等式 :)

无论如何,作为临时迁移步骤,我希望允许用户仍然提供遗留文档(也就是使用 DTD,因此没有命名空间)。我仍将使用 XSD 验证文档,因此 DTD 将被忽略。除了 StAX(也不是 JAXB)似乎不喜欢非命名空间文档之外,这是有效的。我尝试禁用命名空间支持(使用 javax.xml.stream.isNamespaceAware),但这没有任何效果。将 xmlns 显式添加到文档根目录修复了该问题,因此我相当确信这是一个命名空间问题。

有没有办法使用 StAX XMLEventReader 来“引入”默认命名空间?类似 this approach 的东西(这是 SAX 特定的),但对于 StAX ...

或者关于如何实现这一目标的任何其他想法?

示例文档如下所示:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.cid">
...
</hibernate-mapping>

我目前用来阅读这些文件的代码是:
public JaxbRoot unmarshal(InputStream stream, Origin origin) {
try {
XMLEventReader staxReader = staxFactory().createXMLEventReader( stream );
try {
return unmarshal( staxReader, origin );
}
finally {
try {
staxReader.close();
}
catch ( Exception ignore ) {
}
}
}
catch ( XMLStreamException e ) {
throw new MappingException( "Unable to create stax reader", e, origin );
}
}

private XMLInputFactory staxFactory;

private XMLInputFactory staxFactory() {
if ( staxFactory == null ) {
staxFactory = buildStaxFactory();
}
return staxFactory;
}

@SuppressWarnings( { "UnnecessaryLocalVariable" })
private XMLInputFactory buildStaxFactory() {
XMLInputFactory staxFactory = XMLInputFactory.newInstance();
// tried with and without, no effect
//staxFactory.setProperty( "javax.xml.stream.isNamespaceAware", false );
return staxFactory;
}

@SuppressWarnings( { "unchecked" })
private JaxbRoot unmarshal(XMLEventReader staxEventReader, final Origin origin) {
XMLEvent event;
try {
event = staxEventReader.peek();
while ( event != null && !event.isStartElement() ) {
staxEventReader.nextEvent();
event = staxEventReader.peek();
}
}
catch ( Exception e ) {
throw new MappingException( "Error accessing stax stream", e, origin );
}

if ( event == null ) {
throw new MappingException( "Could not locate root element", origin );
}

final Schema validationSchema;
final Class jaxbTarget;

final String elementName = event.asStartElement().getName().getLocalPart();

if ( "entity-mappings".equals( elementName ) ) {
final Attribute attribute = event.asStartElement().getAttributeByName( ORM_VERSION_ATTRIBUTE_QNAME );
final String explicitVersion = attribute == null ? null : attribute.getValue();
validationSchema = validateXml ? resolveSupportedOrmXsd( explicitVersion ) : null;
jaxbTarget = JaxbEntityMappings.class;
}
else {
validationSchema = validateXml ? hbmSchema() : null;
jaxbTarget = JaxbHibernateMapping.class;
}

final Object target;
final ContextProvidingValidationEventHandler handler = new ContextProvidingValidationEventHandler();
try {
JAXBContext jaxbContext = JAXBContext.newInstance( jaxbTarget );
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema( validationSchema );
unmarshaller.setEventHandler( handler );
target = unmarshaller.unmarshal( staxEventReader );
}
catch ( JAXBException e ) {
throw new MappingException( ... );
}

return new JaxbRoot( target, origin );
}

在我的测试中,DTD 存在与否没有任何影响。就像我之前说的,简单地改变
<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.cid">


<hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping" package="org.hibernate.test.abstractembeddedcomponents.cid">

修复了我看到的故障,它们是:
[org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'hibernate-mapping'.]
at ...
Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'hibernate-mapping'.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:195)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:131)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:384)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:318)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.handleStartElement(XMLSchemaValidator.java:1916)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator.startElement(XMLSchemaValidator.java:705)
at com.sun.org.apache.xerces.internal.jaxp.validation.ValidatorHandlerImpl.startElement(ValidatorHandlerImpl.java:550)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.ValidatingUnmarshaller.startElement(ValidatingUnmarshaller.java:78)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXEventConnector.handleStartElement(StAXEventConnector.java:247)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXEventConnector.bridge(StAXEventConnector.java:116)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:394)
... 27 more

最佳答案

这可以通过实现一个过滤器来完成,该过滤器将默认命名空间声明添加到第一个(即根)StartELement事件。 StAX 已经提供了 EventReaderDelegate实用程序类,其中 peek()nextEvent()方法需要被覆盖。

这是代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.EventReaderDelegate;

/**
* Filter adding default namespace declaration to root element.
*/
public class NamespaceAddingEventReader extends EventReaderDelegate {
private final XMLEventFactory factory = XMLEventFactory.newInstance();
private final String namespaceURI;

private int startElementCount = 0;

public NamespaceAddingEventReader(XMLEventReader reader, String namespaceURI) {
super(reader);
this.namespaceURI = namespaceURI;
}

/**
* Duplicate event with additional namespace declaration.
* @param startElement
* @return event with namespace
*/
private StartElement withNamespace(StartElement startElement) {
List<Object> namespaces = new ArrayList<Object>();
namespaces.add(factory.createNamespace(namespaceURI));
Iterator<?> originalNamespaces = startElement.getNamespaces();
while (originalNamespaces.hasNext()) {
namespaces.add(originalNamespaces.next());
}
return factory.createStartElement(
new QName(namespaceURI, startElement.getName().getLocalPart()),
startElement.getAttributes(),
namespaces.iterator());
}

@Override
public XMLEvent nextEvent() throws XMLStreamException {
XMLEvent event = super.nextEvent();
if (event.isStartElement()) {
if (++startElementCount == 1) {
return withNamespace(event.asStartElement());
}
}
return event;
}

@Override
public XMLEvent peek() throws XMLStreamException {
XMLEvent event = super.peek();
if (startElementCount == 0 && event.isStartElement()) {
return withNamespace(event.asStartElement());
} else {
return event;
}
}
}

要了解如何使用它,让我们使用事件 API 将一些没有命名空间声明的 XML 复制到 System.out:
StringReader xml = new StringReader("<?xml version='1.0'?><alice>bob</alice>");
XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(xml);
reader = new NamespaceAddingEventReader(reader, "http://foo");
XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(System.out);
writer.add(reader);
writer.flush();

运行代码将打印
<?xml version='1.0' encoding='UTF-8'?><alice xmlns="http://foo">bob</alice>

关于StAX 和命名空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10653416/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com