2012-05-18 14 views
5

मैं कुछ कोड को DOM (jDOM के माध्यम से) का उपयोग करने के बजाय StAX का उपयोग करने के लिए कनवर्ट करने का प्रयास कर रहा हूं। साथ ही मैं डीटीडी-आधारित सत्यापन से XSD_based सत्यापन में माइग्रेट कर रहा हूं। ओह, और सिर्फ अच्छे उपाय के लिए मैं समीकरण में जेएक्सबी पेश कर रहा हूं :)स्थिर और नामस्थान

वैसे भी, अंतरिम माइग्रेशन चरण के रूप में मैं उपयोगकर्ताओं को अभी भी विरासत दस्तावेज (उर्फ, डीटीडी का उपयोग करके और इसलिए कोई नामस्थान नहीं) प्रदान करने की अनुमति देना चाहता हूं। मैं अभी भी एक्सएसडी का उपयोग कर दस्तावेज़ को मान्य कर दूंगा, इसलिए डीटीडी को नजरअंदाज कर दिया जाएगा। यह काम करता है कि 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 
+0

स्टैक्स का क्या कार्यान्वयन? जेडीके बिल्टिन? Woodstox? अन्य? – bmargulies

+0

जेडीके मुझे लगता है में बनाया गया। मैं किसी अन्य को कॉन्फ़िगर करने के लिए कुछ भी विशेष नहीं कर रहा हूं। –

+1

लोग नामस्थानों के बिना हर समय दस्तावेज़ पढ़ने के लिए स्टैक्स का उपयोग करते हैं। आपको हमें कुछ कोड और कुछ एक्सएमएल दिखाने की जरूरत है। – bmargulies

उत्तर

6

यह एक फिल्टर जो एक कहते हैं को लागू करने से किया जा सकता है पहले नाम (यानी रूट) 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; 
     } 
    } 
} 

कि यह कैसे किया जाता है देखने के लिए, के नाम स्थान घोषणा के बिना कुछ 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> 
+0

मैं सभी तत्वों के लिए नामस्थान लागू करने के लिए समाप्त हुआ, सिर्फ रूट ही नहीं। लेकिन यह काम किया! धन्यवाद –

संबंधित मुद्दे