首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >StAX和名称空间

StAX和名称空间
EN

Stack Overflow用户
提问于 2012-05-18 21:19:21
回答 1查看 8.3K关注 0票数 5

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

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

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

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

示例文档如下所示:

代码语言:javascript
复制
<?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>

我目前用来读取这些文档的代码是:

代码语言:javascript
复制
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存在与否没有任何影响。就像我之前说的,简单的改变

代码语言:javascript
复制
<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.cid">

代码语言:javascript
复制
<hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping" package="org.hibernate.test.abstractembeddedcomponents.cid">

修复了我看到的故障,这些故障是:

代码语言:javascript
复制
[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
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-05-18 23:36:50

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

代码如下:

代码语言:javascript
复制
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;
        }
    }
}

为了了解它是如何使用的,让我们使用event API将一些不带名称空间声明的XML复制到System.out:

代码语言:javascript
复制
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();

运行代码将打印

代码语言:javascript
复制
<?xml version='1.0' encoding='UTF-8'?><alice xmlns="http://foo">bob</alice>
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10653416

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档