Dengues Studio: Google Group:http://groups.google.com/group/dengues; QQ Group:24885404.
          解析一個XML文件有很多方法,最常用的就是Dom4j,和JDOM.這個我們就是講使用Xalan來解析一個XML文件.
          先有個xml文件如下:
          <?xml version="1.0" encoding="UTF-8"?>
          <a:main xmlns="test0" xmlns:a="testt" xmlns:b="test2">
              
          <a:node>
                  
          <a:nodeA1>A1</a:nodeA1>
                  
          <a:nodeA2>
                      
          <b:nodeB>B1</b:nodeB>
                      
          <b:nodeB>B2</b:nodeB>
                  
          </a:nodeA2>
              
          </a:node>
              
          <node>
                  
          <nodeD/>
              
          </node>
          </a:main> 
          這個一個簡單的xml文件.但是我們現在要用xpath來查找
          "/a:main/node"
          一般你是一個很復雜的表達式,這個xpath是經過簡化的.簡化的代碼你可以使用如下:
           1 private static String simplifyXPathExpression(String xpathExpression) {
           2 
           3         Perl5Matcher matcher = new Perl5Matcher();
           4 
           5         Perl5Compiler compiler = new Perl5Compiler();
           6 
           7         Pattern pattern = null;
           8         try {
           9             pattern = compiler.compile("(.*)/\\s*\\w+\\s*(/(\\.\\.|parent))(.*)");
          10         } catch (MalformedPatternException e) {
          11             ExceptionHandler.process(e);
          12         }
          13 
          14         Perl5Substitution substitution = new Perl5Substitution("$1$4", Perl5Substitution.INTERPOLATE_ALL);
          15 
          16         int lengthOfPreviousXPath = 0;
          17 
          18         do {
          19             lengthOfPreviousXPath = xpathExpression.length();
          20             if (matcher.matches(xpathExpression, pattern)) {
          21                 xpathExpression = Util.substitute(matcher, pattern, substitution, xpathExpression, Util.SUBSTITUTE_ALL);
          22             }
          23         } while (xpathExpression.length() != lengthOfPreviousXPath);
          24 
          25         return xpathExpression;
          26     }
          這里使用的org.apache.oro.text.regex包里面的類.
          現在就開始解析一個XML文件:在Dengues 項目中的org.dengues.commons.jdk.xpath.ComplexXPathUsedXalan類.
           1 public ComplexXPathUsedXalan(String xmlFilename) {
           2         xmlInput = new File(xmlFilename);
           3         if (!xmlInput.exists()) {
           4             throw new RuntimeException("Specified file does not exist!");
           5         }
           6         try {
           7             DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
           8             docFactory.setNamespaceAware(true);
           9             DocumentBuilder builder = docFactory.newDocumentBuilder();
          10             document = builder.parse(xmlInput);
          11             initLastNodes(document.getDocumentElement());
          12             prefixToNamespace.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
          13         } catch (ParserConfigurationException e) {
          14             e.printStackTrace();
          15         } catch (IOException e) {
          16             e.printStackTrace();
          17         } catch (SAXException exception) {
          18             exception.printStackTrace();
          19         }
          20         XPathFactory factory = XPathFactory.newInstance();
          21         xPath = factory.newXPath();
          22         xPath.setNamespaceContext(getNamespaceContext());
          23     }
          這是類的構造函數.這里要注意的幾個地方就是:1.第8行設置這個DocFactory支持Namespace.這里一定要設置否則你將不能找所需要的節點.
          2.在第12行,此前我新建了一個Map用來存儲Prefix和Namespace的對應關系:
          1 private final Map<String, String> prefixToNamespace = new HashMap<String, String>();
          在12行就是講XML對應Namespace設置進去.
          3.第11行,它的作用的就是取出所有的Prefix和Namespace.代碼如下:
           1 private void initLastNodes(Node node) {
           2         NodeList childNodes = node.getChildNodes();
           3         int length = childNodes.getLength();
           4         int type = node.getNodeType();
           5         if (type == Node.ELEMENT_NODE) {
           6             setPrefixToNamespace(node);
           7         }
           8         for (int i = 0; i < length; i++) {
           9             Node item = childNodes.item(i);
          10             if (item.getChildNodes().getLength() > 0) {
          11                 initLastNodes(item);
          12             }
          13         }
          14     }
          15 
          16     /**
          17      * DOC qzhang Comment method "setPrefixToNamespace".
          18      * 
          19      * @param node
          20      */
          21     private void setPrefixToNamespace(Node node) {
          22         NamedNodeMap nnm = node.getAttributes();
          23         for (int i = 0; i < nnm.getLength(); i++) {
          24             Node attr = nnm.item(i);
          25             String aname = attr.getNodeName();
          26             boolean isPrefix = aname.startsWith(XMLConstants.XMLNS_ATTRIBUTE + ":");
          27             if (isPrefix || aname.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
          28                 int index = aname.indexOf(':');
          29                 String p = isPrefix ? aname.substring(index + 1) : XMLConstants.NULL_NS_URI;
          30                 prefixToNamespace.put(p, attr.getNodeValue());
          31             }
          32         }
          33     }
          這樣的就可以去到當前XML的所有Prefix的所有節點.
          4.其實去Prefix和Namepace還由另外的API:com.sun.org.apache.xml.interal.utils.PrefixResloverDefault,你可以使用它:
          1  PrefixResolverDefault resolverDefault = new PrefixResolverDefault(document.getDocumentElement());
          2        String namespace = resolverDefault.getNamespaceForPrefix(prefix);
          你可以使用這種方法.但是我覺得這種方法在大部分情況下解決不了問題.原因:(1)當前你只有document的根結點.也就是說它只能得到根結點定義的Namespace.(2)當根結點也使用了Namespace的話(a:main),解析就有問題.
          5.構造函數的22行設置一個NamespaceContext,代碼如下:
           1 private NamespaceContext getNamespaceContext() {
           2         return new NamespaceContext() {
           3 
           4             public String getNamespaceURI(String prefix) {
           5                 String namespaceForPrefix = getNamespaceForPrefix(prefix);
           6                 return namespaceForPrefix;
           7 
           8             }
           9 
          10             public java.util.Iterator getPrefixes(String val) {
          11                 return null;
          12             }
          13 
          14             public String getPrefix(String uri) {
          15                 return null;
          16             }
          17         };
          18     }
          19 
          20     /**
          21      * DOC qzhang Comment method "getNamespaceForPrefix".
          22      * 
          23      * @param prefix
          24      * @return
          25      */
          26     protected String getNamespaceForPrefix(String prefix) {
          27         String namespace = prefixToNamespace.get(prefix);
          28         if (namespace != null) {
          29             return namespace;
          30         }
          31         return getDefaultNamespace();
          32     }
          33 
          34     private String getDefaultNamespace() {
          35         Node parent = document.getDocumentElement();
          36         int type = parent.getNodeType();
          37         if (type == Node.ELEMENT_NODE) {
          38             NamedNodeMap nnm = parent.getAttributes();
          39             for (int i = 0; i < nnm.getLength(); i++) {
          40                 Node attr = nnm.item(i);
          41                 String aname = attr.getNodeName();
          42                 if (aname.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
          43                     return attr.getNodeValue();
          44                 }
          45             }
          46         }
          47         return XMLConstants.NULL_NS_URI;
          48     }
          這個總要就是取Namespace值,但是當你輸入Prefix不在Map里面的時候,它將取默認的Namespace,也就是在getDefaultNamespace().
          現在就是最后要提供一個API:
           1 public Object parseXPath(String expression, QName name) {
           2         try {
           3             expression = addDefaultPrefix(expression);
           4             XPathExpression xexpr = xPath.compile(expression);
           5             Object cd = xexpr.evaluate(getDocumnent(), name);
           6             return cd;
           7         } catch (XPathExpressionException e) {
           8             e.printStackTrace();
           9         }
          10         return null;
          11     }
          12 
          13     private String addDefaultPrefix(String xPathExpression) {
          14         if (XMLConstants.NULL_NS_URI.equals(getDefaultNamespace())) {
          15             return xPathExpression;
          16         } else {
          17             StringBuilder expr = new StringBuilder();
          18             String[] split = xPathExpression.split("/");
          19             for (String string : split) {
          20                 if (!string.equals(""&& string.indexOf(':'== -1 && string.indexOf('.'== -1) {
          21                     expr.append(XMLConstants.DEFAULT_NS_PREFIX + ":");
          22                 }
          23                 expr.append(string + "/");
          24             }
          25             if (split.length > 0) {
          26                 expr.deleteCharAt(expr.length() - 1);
          27             }
          28             return expr.toString();
          29         }
          30     }

          這個就是提供的解析xpath表達是的接口.大家會看到了要執行一個添加addDefaultPrefix()的函數,它的作用就是在
          當存在默認Namespace的時候,添加一個默認的Prefix.這樣的問題解決了.
          再看看測試的代碼:
           1 private static void testXPath() {
           2         ComplexXPathUsedXalan complexXPath = new ComplexXPathUsedXalan("c:/SimpleNamespace.xml");
           3         String expression = "/a:main/node";
           4         try {
           5             NodeList nodes = (NodeList) complexXPath.parseXPath(expression, XPathConstants.NODESET);
           6             System.out.println("length: " + nodes.getLength());
           7             for (int i = 0; i < nodes.getLength(); i++) {
           8                 Node item = nodes.item(i);
           9                 System.out.println("Name:" + item.getNodeName() + " value:" + item.getNodeValue());
          10             }
          11         } catch (Exception e) {
          12             e.printStackTrace();
          13         }
          14     }
          這樣就可以得到打印結果:
          1 length: 1
          2 Name:node value:null
          就是這樣了.歡迎大家指正.



          Dengues論壇(http://groups.google.com/group/dengues/),一個很好的Eclipse開發者樂園.

          Feedback

          # re: [Dengues]關于使用XPath的勸解.帶有Namespace的.  回復  更多評論   

          2007-11-06 16:46 by zDevil(Dengues Studio)
          不好意思,標題都寫錯了,應該是講解.^_^!

          # re: [Dengues]關于使用XPath的勸解.帶有Namespace的.  回復  更多評論   

          2007-11-06 17:41 by li
          恰好今天在項目中也用到dom4j和jaxen,不過在Xpath時候遇到不能能解析命名空間的的問題,沒有好的辦法就去掉了命名空間,呵呵
          以后重新試試

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          Dengues Studio: Google Group:http://groups.google.com/group/dengues; QQ Group:24885404.
          主站蜘蛛池模板: 蕉岭县| 黄大仙区| 水城县| 福安市| 武定县| 钦州市| 河西区| 云和县| 古丈县| 乾安县| 舞阳县| 安达市| 新蔡县| 克东县| 朝阳区| 松潘县| 上饶县| 苍山县| 陵川县| 宜丰县| 壤塘县| 杭州市| 深州市| 彩票| 深圳市| 潞西市| 乐都县| 江源县| 徐汇区| 常宁市| 嘉峪关市| 杨浦区| 光泽县| 德昌县| 凤庆县| 加查县| 赤水市| 兰溪市| 乐都县| 义马市| 磐安县|