2012-05-18 8 views
5

DOMを使用して(jDOM経由で)StAXを使用するコードを変換しようとしています。同時に、DTDベースの検証からXSDベースの検証に移行しています。ああ、良い尺度のために、私はJAXBを方程式に導入しています:)StAXと名前空間

とにかく、暫定的な移行手順として、私はユーザーにレガシードキュメント(別名、名前空間を使用しない)を提供できるようにしたいと考えています。私はまだ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> 

私は現在、これらの文書を読むために使用していますコードは:私のテストのDTDがいるかいないで

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

は効果がありません。

[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 
+0

StaXの実装は? JDK組み込みですか?ウッドストックス?その他? – bmargulies

+0

JDKが内蔵されていると思います。私は別のものを設定するために特別なことをしていません。 –

+1

人々はいつも名前空間のないドキュメントを読むためにstaxを使います。いくつかのコードと少しのXMLを表示する必要があります。 – bmargulies

答えて

6

これは、追加のフィルタを実装することによって行うことができますなど、私は単純です

<hibernate-mapping package="org.hibernate.test.abstractembeddedcomponents.cid"> 

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

に修正私が見るの失敗を、変更前と述べましたデフォルトの名前空間宣言を最初の(つまりルート) StartELementイベントに設定します。 StAXは既にと nextEvent()のメソッドをオーバーライドする必要がある EventReaderDelegateユーティリティクラスを提供しています。ここで

はコードです:

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を使用してSystem.outに名前空間宣言せずに、いくつかのXMLをコピーしてみましょう:

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> 
+0

私は根元だけでなく、すべての要素に名前空間を適用する必要がありました。しかし、それは働いた!ありがとう –