2011-03-28 26 views
6

Son bir gün, aşağıdaki belgeden bir XML düğümü ayıklamak için çalıştım ve çalışmasını sağlamak için XML Ad alanı nüanslarını yakalayamıyorum. XPath, XML Ad Alanları ve Java

XML dosyası

işte toplamda yayınlamak için büyük olmaktır beni ilgilendiren kısmı ise:

<?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> 

belge devam eder ve iyi aşağı kadar oluşturulur. "Documentnbr" etiketinden "sayı" özniteliğini çıkarmaya çalışıyorum (alttan üç tane).

bunu yapmak kullanıyorum kod şuna benzer:

/*** 
    * 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 benim XPath ifadesidir ve XFDLNamespaceContext NamespaceContext uygular ve şöyle görünür Nerede:

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

} 

Ben ettik birçok farklı XPath sorguları denedim ama çalışması gerekir böyle hissetmekten tutmak:

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

Maalesef işe yaramıyor ve sürekli olarak null bir getiri elde ediyorum.

here, here ve here kodlarını okudum, ancak bu çalışma için bana yardımcı olacak hiçbir şey yeterince aydınlanmadı.

Bunu anladığımda yüz-avuç için gideceğimden neredeyse neredeyse eminim ama neyi kaçırdığımı bilmiyorum.

Tüm bunları okuduğunuz için teşekkürler ve yardım için şimdiden teşekkürler.

-Andy

cevap

5

Aha, senin ifadesini hata ayıklamak için çalıştı + çalışmaya aldım. Birkaç şeyi kaçırdın. Bu XPath ifadesi bunu yapmalıdır:

/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number 
  1. Benim nedense ifadede herhangi ad kullanmak için ihtiyacı kadar sona ermedi
  2. kök elemanı (bu durumda XFDL) dahil etmek gerekir. Emin değilim neden. Bu durumda, NamespaceContext.getNamespaceURI() asla çağrılmaz. instance'u xforms:instance ile değiştirirseniz, getNamespaceURI(), giriş argümanı olarak xforms ile bir kez çağrılır, ancak program bir istisna atar.
  3. Özellik değerlerinin sözdizimi @attr, [attr] değil.

Benim tam örnek kod:

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

mükemmel çözüm, hala oldukça ad alanlarını anlamıyorum ama en durumunda açıkça bunu kullanabileceğiniz ek ad alanlarını tanımlamak gerekir En azından kod şimdi çalışıyor. Çok teşekkürler. – MrWizard54

+8

@ Jason: "Bir sebepten dolayı ifadede herhangi bir isim kullanmak zorunda kalmadım." Standart Java uygulamasında, DocumentBuilderFactory varsayılan olarak namespace-_unaware_ ayrıştırıcıları üretir. DocumentBuilder'i oluşturmadan önce 'dbfac.SetNamespaceAware (true)' ifadesi, sonuçları değiştirebilir. –

3

SAX (XPath alternatif) sürümü:

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

buna ( olması gerektiği gibi ad ile XPath kullanmak daha karmaşıktır bkz benim fikrim).

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

Sen here (GPL lisansı) den NamespaceContextMap sınıfını (benim değil) alabilirsiniz: İşte benim (basit) kodudur. Ayrıca 6376058 da var.

+0

DOM/XPath'ı uygulamada başka bir yerde kullanmamış olsaydım, bu rotaya giderdim, ancak şu anda başka yöne gittim. Bir uygulamanın içine karıştırma konusunda geleneksel bir bilgelik var mı? – MrWizard54

+0

DOM/XPath'i ikinci kodla kullanabilirsiniz (xpath.evalute Document nesnesini de alır). Benim düşünceme göre NamespaceContextMap sınıfını kullanmak daha iyi (JDO'da imho olmalı). –

2

XPathAPI kitaplığına bir göz atın. Özellikle ad alanlarıyla uğraşırken, düşük düzeyli Java API'sini karıştırmadan XPath kullanmanın daha basit bir yoludur.

kod

number özelliği olacaktır almak için:

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

Ad otomatik kök düğümü (bu durumda doc) ekstrakte edilir.

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

(Yasal Uyarı::. Kütüphanenin yazarım)

İlgili konular