2011-03-28 15 views
6

मैंने पिछले दिन एक दस्तावेज़ को एक XML नोड निकालने का प्रयास किया है और इसे काम करने के लिए एक्सएमएल नेमस्पेस की बारीकियों को समझने में असमर्थ हूं।XPath, एक्सएमएल नेमस्पेस और जावा

एक्सएमएल फ़ाइल इसलिए यहाँ कुल में पोस्ट करने के लिए बड़ी है भाग है कि मुझे चिंता है:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> 
<XFDL xmlns="http://www.PureEdge.com/XFDL/6.5" xmlns:custom="http://www.PureEdge.com/XFDL/Custom" xmlns:designer="http://www.PureEdge.com/Designer/6.1" xmlns:pecs="http://www.PureEdge.com/PECustomerService" xmlns:xfdl="http://www.PureEdge.com/XFDL/6.5"> 
    <globalpage sid="global"> 
     <global sid="global"> 
     <xmlmodel xmlns:xforms="http://www.w3.org/2003/xforms"> 
      <instances> 
       <xforms:instance id="metadata"> 
        <form_metadata> 
        <metadataver version="1.0"/> 
        <metadataverdate> 
         <date day="05" month="Jul" year="2005"/> 
        </metadataverdate> 
        <title> 
         <documentnbr number="2062" prefix.army="DA" scope="army" suffix=""/> 
         <longtitle>HAND RECEIPT/ANNEX NUMBER </longtitle> 
        </title> 

दस्तावेज़ जारी है और अच्छी तरह से सभी तरह से नीचे ही बना है। मैं "documentnbr" टैग (नीचे से तीन) से "संख्या" विशेषता निकालने का प्रयास कर रहा हूं।

कोड है कि मैं यह करने के लिए उपयोग कर रहा हूँ इस तरह दिखता है:

/*** 
    * Locates the Document Number information in the file and returns the form number. 
    * @return File's self-declared number. 
    * @throws InvalidFormException Thrown when XPath cannot find the "documentnbr" element in the file. 
    */ 
    public String getFormNumber() throws InvalidFormException 
    { 
     try{ 
      XPath xPath = XPathFactory.newInstance().newXPath(); 
      xPath.setNamespaceContext(new XFDLNamespaceContext()); 

      Node result = (Node)xPath.evaluate(QUERY_FORM_NUMBER, doc, XPathConstants.NODE); 
      if(result != null) { 
       return result.getNodeValue(); 
      } else { 
       throw new InvalidFormException("Unable to identify form."); 
      } 

     } catch (XPathExpressionException err) { 
      throw new InvalidFormException("Unable to find form number in file."); 
     } 

    } 

कहाँ QUERY_FORM_NUMBER मेरी XPath अभिव्यक्ति है, और XFDLNamespaceContext NamespaceContext लागू करता है और इस तरह दिखता है:

public class XFDLNamespaceContext implements NamespaceContext { 

    @Override 
    public String getNamespaceURI(String prefix) { 
     if (prefix == null) throw new NullPointerException("Invalid Namespace Prefix"); 
     else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) 
      return "http://www.PureEdge.com/XFDL/6.5"; 
     else if ("custom".equals(prefix)) 
      return "http://www.PureEdge.com/XFDL/Custom"; 
     else if ("designer".equals(prefix)) 
      return "http://www.PureEdge.com/Designer/6.1"; 
     else if ("pecs".equals(prefix)) 
      return "http://www.PureEdge.com/PECustomerService"; 
     else if ("xfdl".equals(prefix)) 
      return "http://www.PureEdge.com/XFDL/6.5";  
     else if ("xforms".equals(prefix)) 
      return "http://www.w3.org/2003/xforms"; 
     else  
      return XMLConstants.NULL_NS_URI; 
    } 

    @Override 
    public String getPrefix(String arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public Iterator getPrefixes(String arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

} 

मैं मैंने कई अलग-अलग XPath प्रश्नों का प्रयास किया है, लेकिन मुझे लगता है कि यह काम करना चाहिए:

protected static final String QUERY_FORM_NUMBER = 
     "/globalpage/global/xmlmodel/xforms:instances/instance" + 
     "/form_metadata/title/documentnbr[number]"; 

दुर्भाग्यवश यह काम नहीं करता है और मुझे लगातार शून्य वापसी मिलती है।

मैंने here, here, और here पढ़ने की उचित मात्रा में काम किया है, लेकिन कुछ भी काम करने में मेरी सहायता करने के लिए पर्याप्त रूप से रोशनी साबित हुई है।

मैं लगभग सकारात्मक हूं कि जब मैं इसे समझता हूं तो मैं चेहरे पर जा रहा हूं लेकिन मैं वास्तव में बुद्धिमान हूं कि मैं क्या खो रहा हूं।

इस सब के माध्यम से पढ़ने के लिए धन्यवाद और सहायता के लिए अग्रिम धन्यवाद।

-Andy

उत्तर

5

अहा, मैं अपने अभिव्यक्ति डिबग करने की कोशिश की + काम करने के लिए मिल गया। आप कुछ चीजों को याद किया। यह XPath अभिव्यक्ति यह करना चाहिए:

/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number 
  1. आप मूल तत्व (इस मामले में XFDL)
  2. मैं किसी कारण से अभिव्यक्ति में कोई नामस्थान का उपयोग करने की आवश्यकता होगी, खत्म नहीं हुई शामिल करने के लिए की जरूरत है। यकीन नहीं है कि क्यों। यदि यह मामला है, तो नेमस्पेस कॉन्टेक्स्ट .getNamespaceURI() कभी नहीं बुलाया जाता है। यदि मैं instance को xforms:instance के साथ प्रतिस्थापित करता हूं तो इनपुट नाम के रूप में xforms के साथ नामनामसिस() को एक बार कॉल किया जाता है, लेकिन प्रोग्राम अपवाद फेंकता है।
  3. विशेषता मानों के लिए वाक्यविन्यास @attr है, [attr] नहीं।

मेरे पूरा नमूना कोड:

import java.io.File; 
import java.io.IOException; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 

import javax.xml.XMLConstants; 
import javax.xml.namespace.NamespaceContext; 
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.parsers.ParserConfigurationException; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathExpressionException; 
import javax.xml.xpath.XPathFactory; 

import org.w3c.dom.Document; 
import org.w3c.dom.Node; 
import org.xml.sax.SAXException; 

public class XPathNamespaceExample { 
    static public class MyNamespaceContext implements NamespaceContext { 
     final private Map<String, String> prefixMap; 
     MyNamespaceContext(Map<String, String> prefixMap) 
     { 
      if (prefixMap != null) 
      { 
       this.prefixMap = Collections.unmodifiableMap(new HashMap<String, String>(prefixMap)); 
      } 
      else 
      { 
       this.prefixMap = Collections.emptyMap(); 
      } 
     } 
     public String getPrefix(String namespaceURI) { 
      // TODO Auto-generated method stub 
      return null; 
     } 
     public Iterator getPrefixes(String namespaceURI) { 
      // TODO Auto-generated method stub 
      return null; 
     } 
     public String getNamespaceURI(String prefix) { 
       if (prefix == null) throw new NullPointerException("Invalid Namespace Prefix"); 
       else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) 
        return "http://www.PureEdge.com/XFDL/6.5"; 
       else if ("custom".equals(prefix)) 
        return "http://www.PureEdge.com/XFDL/Custom"; 
       else if ("designer".equals(prefix)) 
        return "http://www.PureEdge.com/Designer/6.1"; 
       else if ("pecs".equals(prefix)) 
        return "http://www.PureEdge.com/PECustomerService"; 
       else if ("xfdl".equals(prefix)) 
        return "http://www.PureEdge.com/XFDL/6.5";  
       else if ("xforms".equals(prefix)) 
        return "http://www.w3.org/2003/xforms"; 
       else  
        return XMLConstants.NULL_NS_URI; 
     } 


    } 

    protected static final String QUERY_FORM_NUMBER = 
     "/XFDL/globalpage/global/xmlmodel/xforms:instances/instance" + 
     "/form_metadata/title/documentnbr[number]"; 

    public static void main(String[] args) { 
     try 
     { 
      DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); 
      DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); 
      Document doc = docBuilder.parse(new File(args[0])); 
      System.out.println(extractNodeValue(doc, "/XFDL/globalpage/@sid")); 
      System.out.println(extractNodeValue(doc, "/XFDL/globalpage/global/xmlmodel/instances/instance/@id")); 
      System.out.println(extractNodeValue(doc, "/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number")); 
     } catch (SAXException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (ParserConfigurationException e) { 
      e.printStackTrace(); 
     } 
    } 

    private static String extractNodeValue(Document doc, String expression) { 
     try{ 

      XPath xPath = XPathFactory.newInstance().newXPath(); 
      xPath.setNamespaceContext(new MyNamespaceContext(null)); 

      Node result = (Node)xPath.evaluate(expression, doc, XPathConstants.NODE); 
      if(result != null) { 
       return result.getNodeValue(); 
      } else { 
       throw new RuntimeException("can't find expression"); 
      } 

     } catch (XPathExpressionException err) { 
      throw new RuntimeException(err); 
     } 
    } 
} 
+0

बिल्कुल सही समाधान है, मैं अभी भी काफी नामस्थान समझ में नहीं आता पर लेकिन मामले में आप स्पष्ट रूप से अतिरिक्त नामस्थान आप इस का उपयोग कर सकते परिभाषित करने की जरूरत कम से कम कोड अब काम करता है। बहुत धन्यवाद। – MrWizard54

+8

@ जेसन: "मुझे कुछ कारणों से अभिव्यक्ति में किसी भी नामस्थान का उपयोग करने की ज़रूरत नहीं है।" मानक जावा कार्यान्वयन में, DocumentBuilderFactory डिफ़ॉल्ट रूप से नामस्थान-_unaware_ पार्सर्स का उत्पादन करता है। दस्तावेज़बिल्डर उत्पन्न करने से पहले 'dbfac.SetNamespaceAware (true) 'जोड़ना परिणाम बदल सकता है। –

3

SAX (XPath करने के लिए वैकल्पिक) संस्करण:

SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); 
final String[] number = new String[1]; 
DefaultHandler handler = new DefaultHandler() 
{   
    @Override 
    public void startElement(String uri, String localName, String qName, 
    Attributes attributes) throws SAXException 
    { 
     if (qName.equals("documentnbr")) 
      number[0] = attributes.getValue("number"); 
    } 
}; 
saxParser.parse("input.xml", handler); 
System.out.println(number[0]); 

मैं देख रहा हूँ यह नामस्थान साथ XPath का उपयोग करने के रूप में यह होना चाहिए (और अधिक जटिल है मेरी राय)।

XPath xpath = XPathFactory.newInstance().newXPath(); 

NamespaceContextMap contextMap = new NamespaceContextMap(); 
contextMap.put("custom", "http://www.PureEdge.com/XFDL/Custom"); 
contextMap.put("designer", "http://www.PureEdge.com/Designer/6.1"); 
contextMap.put("pecs", "http://www.PureEdge.com/PECustomerService"); 
contextMap.put("xfdl", "http://www.PureEdge.com/XFDL/6.5"); 
contextMap.put("xforms", "http://www.w3.org/2003/xforms"); 
contextMap.put("", "http://www.PureEdge.com/XFDL/6.5"); 

xpath.setNamespaceContext(contextMap); 
String expression = "//:documentnbr/@number"; 
InputSource inputSource = new InputSource("input.xml"); 
String number; 
number = (String) xpath.evaluate(expression, inputSource, XPathConstants.STRING); 
System.out.println(number); 

आप here (जीपीएल लाइसेंस) से NamespaceContextMap वर्ग (मेरा नहीं) प्राप्त कर सकते हैं: यहाँ मेरी (सरल) कोड है। 6376058 बग भी है।

+0

यदि मैं एप्लिकेशन में कहीं और डीओएम/एक्सपीएथ का उपयोग नहीं कर रहा था तो मैं इस मार्ग पर जाऊंगा, लेकिन वर्तमान में मैं दूसरी दिशा में आगे बढ़ रहा हूं। क्या आवेदन के अंदर मिश्रण पर पारंपरिक ज्ञान है? – MrWizard54

+0

आप दूसरे कोड के साथ डीओएम/एक्सपीएथ का उपयोग कर सकते हैं (xpath.evalute दस्तावेज़ ऑब्जेक्ट भी लेता है)। मेरी राय में नेमस्पेस कॉन्टेक्स्ट मैप क्लास का उपयोग करना बेहतर है (इम्हो जेडीके में होना चाहिए)। –

2

XPathAPI लाइब्रेरी पर एक नज़र डालें। यह निम्न स्तर के जावा एपीआई के साथ गड़बड़ किए बिना XPath का उपयोग करने का एक आसान तरीका है, खासकर जब नामस्थानों से निपटना।

कोड प्राप्त करने के number विशेषता होगा:

String num = XPathAPI.selectSingleNodeAsString(doc, '//documentnbr/@number'); 

नेमस्पेस स्वचालित रूप से रूट नोड (इस मामले में doc) से निकाले जाते हैं।

Map<String, String> nsMap = new HashMap<String, String>(); 
nsMap.put("xforms", "http://www.w3.org/2003/xforms"); 

String num = 
    XPathAPI.selectSingleNodeAsString(doc, '//documentnbr/@number', nsMap); 

(अस्वीकरण::। मैं पुस्तकालय के लेखक हूँ)