共用方式為


跨越不同存放區的 XPathNavigator

XPathNavigator 提供了可以在任何實作 IXPathNavigable 介面的存放區上執行 XPath 查詢的功能。實施 IXPathNavigable 介面的類別為 XPathDocumentXmlDocumentXmlDataDocument

XPathDocument 類別允許在 XML 資料流上的 XPath 查詢。同時它也針對執行 XPath 查詢和 XSLT 轉換方面進行高度最佳化。當最優先考慮 XPath 的效能時,此類別是最適合 XSLT 轉換的類別。

XmlDocument 類別除了提供可編輯 XML 的 DOM 檢視之外,也允許在資料上使用 XPath。XPathDocumentXmlDocument 對相同的 XML 的查詢結果上擁有相同的行為。對於 XPath 查詢而言,XmlDocument 的效能比 XPathDocument 差,這是因為此類別中編輯功能的關係。

XmlDataDocument 類別提供完全相容的 DOM,它和 DataSet 是同步的。它繼承 XmlDocument 類別而被用來取得關聯資料的階層式 XML 檢視。它可用來對儲存於 Dataset 以 XML 呈現的資料進行 Xpath 查詢。如需 XmlDataDocument 的詳細資訊,請參閱使用 XmlDataDocument 同步處裡 DataSet

為了能在這些類別中執行 XPath 查詢,請叫用 (Invoke) CreateNavigator 函式以傳回 XPathNavigator。然後便可以使用 XPathNavigator 在文件上執行 XPath 查詢,如本章節中其他部分中所述。

您也可以擴充 XPathNavigator 類別,以便定義自己的巡覽器。這需要建立虛擬節點結構並實作方法來巡覽這個結構。一旦撰寫了節點結構,便啟用了執行 XPath 查詢的能力。在 XPathNavigator 類別中的 Select 方法已被實作,並且只需要這個結構來執行。下列範例在檔案系統上實作 XPathNavigator

從建立虛擬節點結構開始。這個結構會公開基礎檔案系統,以在 XML Infoset 中包含所有的資訊。這個 XML 結構透過覆寫傳回關於節點資訊之 XPathNavigator 類別中的方法來公開。在這個範例中,目錄和檔案將以項目的方式公開。檔案系統樹狀結構將被鏡射至公開的 XML Infoset 的樹狀結構中。表示目錄的項目會把目錄的區域名稱和建立時間當成屬性。表示檔案的項目會把檔案的區域名稱、建立時間和長度當成屬性。您可以對檔案系統執行查詢,例如,/abc/xml 會選取包含在具有 abc 區域名稱之目錄中,所有區域名稱為 xml 的目錄或檔案。

為了公開這個結構,您必須覆寫下列節點屬性。

  • NodeType
  • LocalName
  • Nane
  • Prefix
  • Value
  • BaseURI
  • IsEmptyElement
  • XmlLang
  • NameTable

為了公開這個結構,您必須覆寫下列屬性存取子 (Accessor)。

  • HasAttributes
  • GetAttributes
  • MoveToAttributes
  • MoveToFirstAttributes
  • MoveToNextAttributes

為了公開這個結構,您必須覆寫下列命名空間存取子。

  • GetNamespace
  • MoveToNamespace
  • MoveToFirstNamespace
  • MoveToNextNamespace

使用這些方法和屬性,可以將基礎結構完全公開為 XML Infoset。

此外,所有的 LocalNameNameSpaceUriPrefix 字串必須加入至由 NameTable 屬性所提供的 NameTable 中。當 LocalNameNamespaceURIPrefix 屬性被傳回時,字串應該是從 NameTable 傳回。名稱之間的比較是透過物件比較完成,而不是透過明顯比較慢的字串比較完成。

此外,用來巡覽這個虛擬節點結構的方法也同樣需要實作。這些方法包括:

  • MoveToNext
  • MoveToPrevious
  • MoveToFirst
  • MoveToFirstChild
  • MoveToParent
  • MoveToRoot
  • MoveTo
  • MoveToId
  • IsSamePosition
  • HasChildren

這些方法將基礎結構公開為樹狀結構。一旦實作了這些方法和屬性,所有 XPath 查詢便可在此虛擬節點集上作業。例如,下列程式碼建立可以巡覽硬碟目錄之自訂的 XPathNavigator。程式以 C:\Program Files 做為巡覽的起點,並反覆在檔案與資料夾之間遊走,這對於某些機器而言,需傳送大量輸出到主控台。您可以修改程式碼以便從您硬碟目前的資料夾開始。

using System;
using System.IO;
using System.Xml;
using System.Xml.XPath;

public class Sample {
    static FileSystemNavigator fsn;
    public static void Main() {
        // This is the hard code for C:\ProgramFiles. You can modify this 
        // to point to a folder on your own machine for test purposes.
        fsn = new FileSystemNavigator(@"C:\Program Files");

        if (fsn.MoveToFirstChild()) {
            ShowNavigator(String.Empty);
        }
    }
    public static void ShowNavigator(string indent) {
        do {
            Console.WriteLine("{0}Name: {1}", indent, fsn.Name);
            if (fsn.HasChildren) {
                fsn.MoveToFirstChild();
                ShowNavigator(indent + "  ");
                fsn.MoveToParent();
            }
        } while (fsn.MoveToNext());
    }
}

public class FileSystemNavigator: XPathNavigator 
    {
      static int[]      NumberOfAttributes = { 2, 3 };
      //The NodeTypes used.
      public enum NodeTypes { Root, Element,  Attribute, Text };

      internal NavigatorState state;
      //Attribute names: For example a type 1 element will have a type 2 attribute named "Length"
      static String[][] AttributeIds       = new String[2][] 
               {
                  new String[] {"Name", "CreationTime"},   
                  new String[] {"Name", "CreationTime", "Length"}  
                };
      NameTable nametable;
      public FileSystemNavigator( String rootNode ) 
      {
          FileSystemInfo document = Directory.CreateDirectory(rootNode);
          nametable = new NameTable();
          nametable.Add(String.Empty);
          //if the Directory does not exist then rootNode must be a file.
          if( !document.Exists )
          {
            //create the file if it does not already exists
            FileStream tempStream = File.Open(rootNode,System.IO.FileMode.OpenOrCreate);
            tempStream.Close();
            document = new FileInfo(rootNode);
       }

         if( document.Exists )
         {
            state = new NavigatorState(document);

         }
         else
         {
            throw (new Exception("Root node must be a directory or a file"));
         }
      }
   
      public FileSystemNavigator( FileSystemInfo document ) 
      {
         nametable = new NameTable();
         nametable.Add(String.Empty);
         if( document.Exists )
         {
                state = new NavigatorState(document);
         
         }
         else
         {
             throw (new Exception("Root node must be a directory or a file"));
         }
         }

    public FileSystemNavigator( FileSystemNavigator navigator ) 
         {
         state = new NavigatorState(navigator.state);
         nametable = (NameTable)navigator.NameTable;
     
     }

       public override XPathNavigator Clone() 
       {
       return new FileSystemNavigator(this);
    }

       //NodeProperties
       public override XPathNodeType NodeType 
    { 
       get 
       {
       switch (state.Node) 
          {
             case NodeTypes.Root :
                 return XPathNodeType.Root;
              case NodeTypes.Element :
                   return XPathNodeType.Element;
              case NodeTypes.Attribute :
                  return XPathNodeType.Attribute;
              case NodeTypes.Text :
                  return XPathNodeType.Text;
           }
           return XPathNodeType.All;
           }
     }
     public override string LocalName 
     {
         
         get 
         {
             nametable.Add(Name);
             return nametable.Get(Name); 
         }
     }

     public override string Name 
       { 
         get 
           { 
             switch (state.Node)
           {
             case NodeTypes.Text :
                  return state.TextValue;
              case NodeTypes.Attribute :
                  return state.AttributeText;
              case NodeTypes.Element :
                   return state.ElementText;
               default :
                   return String.Empty;
                   
            }
        }
     }
     public override string NamespaceURI 
       {
        get 
        {
              return nametable.Get(String.Empty); 
           }
      }
      
      public override string Prefix 
       {
          get { return nametable.Get(String.Empty); }
       }
         public override string Value
      {
          get { 
               return state.TextValue;
                }
      }
      public override String BaseURI 
      {
          get { return String.Empty; } 
      }
      public override bool IsEmptyElement
      {
          get 
         {
             if(state.ElementType == 1) 
                return true;
           else 
               return false;
      }
      }
      
      public override string XmlLang
      {
         get{return "en-us";}
      }
      
      public override XmlNameTable NameTable 
      {
        get 
         { 
            return nametable; 
        }
      }
  
//Attribute Accessors

      public override bool HasAttributes 
      {
         get
         {
             if((state.Node != NodeTypes.Root) && (state.Node != NodeTypes.Attribute) && (state.Node != NodeTypes.Text))
                 return true;
             else
               return false;
      }
      }
      public override string GetAttribute( string localName, string namespaceURI ) 
        {
            if( HasAttributes)
            {
                int i;
                for(i = 0; i <  NumberOfAttributes[state.ElementType]; i++)
                {
                    if( AttributeIds[state.ElementType][i] == localName )
                        break;
                }

                if( i < NumberOfAttributes[state.ElementType] )
                {
                int TempAttribute = state.Attribute;
                NodeTypes TempNodeType  = state.Node;
                state.Attribute = i;
                state.Node = NodeTypes.Attribute;
                String AttributeValue = state.TextValue;
                state.Node =  TempNodeType;
                state.Attribute = TempAttribute;
                return AttributeValue;
                }
            }
            return String.Empty;
        }

        public override bool MoveToAttribute( string localName, string namespaceURI ) 
        {
            if( state.Node == NodeTypes.Attribute )
                MoveToElement();
            if( state.Node == NodeTypes.Element )
            {
                int i;
                for(i = 0; i < AttributeCount; i++)
                    if( AttributeIds[state.ElementType][i] == localName )
                    {
                        state.Attribute = i;
                        state.Node      = NodeTypes.Attribute;
                        return true;
                    }
            }
            return false;
        }

        public override bool MoveToFirstAttribute() 
        {
            if( state.Node == NodeTypes.Attribute )
                MoveToElement();
            if( AttributeCount > 0 )
            {
                state.Attribute = 0;
                state.Node = NodeTypes.Attribute;
                return true;
            }
            return false;
        }
        public override bool MoveToNextAttribute() 
        {
            
            int TempAttribute = -1;
            if( state.Node == NodeTypes.Attribute )
            {
                TempAttribute = state.Attribute;
                MoveToElement();
            }
            if( (TempAttribute + 1) < AttributeCount )
            {
                state.Attribute = TempAttribute + 1;
                state.Node = NodeTypes.Attribute;
                return true;
            }
            state.Node = NodeTypes.Attribute;
            state.Attribute = TempAttribute;
            return false;
        }

        //Namespace Accesors
        public override string GetNamespace(string localname)
        {
            return String.Empty;
        }
        public override bool MoveToNamespace(string Namespace)
        {
            return false;
        }

        public override bool MoveToFirstNamespace(XPathNamespaceScope namespaceScope)
        {
            return false;
        }

        public override bool MoveToNextNamespace(XPathNamespaceScope namespaceScope)
        {
            return false;
        }
        
//Tree Navigation
    
        public override bool MoveToNext() 
        {
            int NextElement = IndexInParent + 1;
            FileSystemNavigator TempState = (FileSystemNavigator) this.Clone();
            if ( MoveToParent() )
            {
                if( MoveToChild(NextElement) )
                    return true;
            }
            this.state = new NavigatorState(TempState.state);
            return false;
        }
        
        public override bool MoveToPrevious() 
        {
            int NextElement = IndexInParent - 1;
            FileSystemNavigator TempState = (FileSystemNavigator) this.Clone();
            if ( MoveToParent() )
            {
                if( MoveToChild(NextElement) )
                    return true;
            }
            this.state = new NavigatorState(TempState.state);
            return false;
        }

        public override bool MoveToFirst() 
        {
            FileSystemNavigator TempState = (FileSystemNavigator) this.Clone();
            if ( MoveToParent() )
            {
                if( MoveToChild(0) )
                    return true;
            }
            this.state = new NavigatorState(TempState.state);
            return false;

        }
        public override bool MoveToFirstChild() 
        {
            FileSystemNavigator TempState = (FileSystemNavigator) this.Clone();
            if( MoveToChild(0) )
                return true;
            this.state = new NavigatorState(TempState.state);
            return false;
        }
    
        public override bool MoveToParent() 
        {
            switch(state.Node)
            {
                case NodeTypes.Root: 
                    return false;
                default:
                    if( state.Root != state.Doc.FullName )
                    {
                        if( state.Doc is DirectoryInfo )
                            state.Doc = ((DirectoryInfo) state.Doc).Parent;
                        else if( state.Doc is FileInfo )
                            state.Doc = ((FileInfo) state.Doc).Directory;
                        state.Node = NodeTypes.Element;
                        state.Attribute = -1;
                        state.ElementType = 0;
                        if( state.Root != state.Doc.FullName )
                        {
                            FileSystemInfo[] FileSystemEnumerator = ( ((DirectoryInfo) state.Doc).Parent).GetFileSystemInfos();
                            for(int i = 0; i < FileSystemEnumerator.Length; i++ )
                        {
                                if( FileSystemEnumerator[i].Name == state.Doc.Name )
                                {
                                state.ElementIndex = i;
                                }
                            }
                        }
                        else
                        {
                            state.ElementIndex = 0;
                        }
                        return true;
                    }
                    else
                    {
                        MoveToRoot();
                        return true;
                    }

        
            }
        }


        public override void MoveToRoot()
        {
            state.Node=NodeTypes.Root;
            state.Doc    = new FileInfo(state.Root);
            state.Attribute    = -1;
            state.ElementType  = -1;
            state.ElementIndex = -1;
        }
        public override bool MoveTo( XPathNavigator other ) 
        {
            if( other is FileSystemNavigator )
            {
                this.state = new NavigatorState( ((FileSystemNavigator) other).state);
                return true;
            }
            return false;
            }
        public override bool MoveToId( string id ) 
        {
            return false;
        }


        public override bool IsSamePosition( XPathNavigator other ) 
        {
            if( other is FileSystemNavigator )
            {
                if( state.Node == NodeTypes.Root )
                {
                    return (((FileSystemNavigator) other).state.Node == NodeTypes.Root);
                }
                else
                {
                    return (state.Doc.FullName == ((FileSystemNavigator) other).state.Doc.FullName);
                }
            }
            return false;
    
        
        }
    
        public override bool HasChildren 
        {
            get 
            { 
                return (ChildCount > 0); 
            }
        }
        


/***************Helper Methods*****************************************/
    
        //This is a helper method. Move the XPathNavigator from an attribute 
      // to its associated element.
        public bool MoveToElement() 
        {
            state.Attribute = -1;
            state.Node      = NodeTypes.Element;
            if ( state.Doc is DirectoryInfo )
                state.ElementType = 0;
            else 
                state.ElementType = 1;
            return true;
        }
        //Gets the index of this node if it is an element or the index of
      // the element node associated with the attribute.
        public int IndexInParent 
        {
            get 
            { 
                return state.ElementIndex; 
            }
        }
    

        
        //Helper method. Move to child i of the current node.
        public bool MoveToChild( int i ) 
        {
            if( i >= 0 )
            {
                    if( state.Node == NodeTypes.Root && i == 0)
                {
                    state.Doc = Directory.CreateDirectory(state.Root);
                    state.ElementType = 0;
                    if( !state.Doc.Exists )
                    {
                        FileStream tempStream = File.Open(state.Root,System.IO.FileMode.OpenOrCreate);
                        tempStream.Close();
                        state.Doc = new FileInfo(state.Root);
                        state.ElementType = 1;

                    }
                    state.Node = NodeTypes.Element;
                    state.Attribute = -1;
                    state.ElementIndex = 0;
                    return true;
                }
                else if( state.Node == NodeTypes.Element && state.ElementType == 0 )
                {
                
                    FileSystemInfo[] DirectoryEnumerator = ( (DirectoryInfo) state.Doc).GetFileSystemInfos();
                
                    if( i < DirectoryEnumerator.Length )
                    {
                        state.Node = NodeTypes.Element;
                        state.Attribute = -1;
                        state.ElementIndex = i;
                        if( DirectoryEnumerator[i] is DirectoryInfo)
                        {
                                state.Doc = DirectoryEnumerator[i];
                                state.ElementType = 0;
                        }
                        else if( DirectoryEnumerator[i] is FileInfo)
                        {
                            state.Doc = DirectoryEnumerator[i];
                            state.ElementType = 1;
                        }
                        return true;
                    }
                }
            }
            return false;
        }
        
        //returns the number of attributes that the current node has
        public int AttributeCount 
        {
            get 
            { 
                if( state.Node != NodeTypes.Root )
                {
                    return NumberOfAttributes[state.ElementType];
                }
                return 0;
            }
        }
        //Helper method. Returns the number of children that the current node has.
        public int ChildCount 
        {
            get 
            { 
                switch(state.Node)
                {
                    case NodeTypes.Root: 
                        return 1;
                    case NodeTypes.Element:
                        if( state.ElementType == 0 )
                        {
                            return (((DirectoryInfo) state.Doc).GetFileSystemInfos()).Length;
                        }
                        return 0;
                    default:
                        return 0;
                }
            }
        }
    
        //This class keeps track of the state the navigator is in.
        internal class NavigatorState
        {
            //Represents the element that the navigator is currently at.
            public FileSystemInfo doc;
            //The directory or file at the top of the tree
            String root;
            //The type of attribute that the current node is. -1 if the
         // navigator is not currently positioned on an attribute.
            public int attribute;
            //elementType of 0 is a directory and elementType of 1 is a file
            public int elementType;
            public int elementIndex;
            //The type of the current node
            public NodeTypes node;

            public NavigatorState(FileSystemInfo document)
            {
                Doc          = document;
                Root         = doc.FullName;
                Node         = NodeTypes.Root;
                Attribute    = -1;
                ElementType  = -1;
                ElementIndex = -1;
        
            }

            public NavigatorState(NavigatorState NavState)
            {
                Doc          = NavState.Doc;
                Root         = NavState.Root;
                Node         = NavState.Node;
                Attribute    = NavState.Attribute;
                ElementType  = NavState.ElementType;
                ElementIndex = NavState.ElementIndex;
        
            }

            public FileSystemInfo Doc
            {
                get 
                {
                    return doc;
                }
                set 
                {
                    doc = value;
                }
            }
    
            public String Root
            {
                get 
                {
                    return root;
                }
                set 
                {
                    root = value;
                }
            }

            public int Attribute
            {
                get 
                {
                    return attribute;
                }
                set 
                {
                    attribute = value;
                }
            }

            public int ElementType
            {
                get 
                {
                    return elementType;
                }
                set 
                {
                    elementType = value;
                }
            }

            public int ElementIndex
            {
                get 
                {
                    return elementIndex;
                }
                set 
                {
                    elementIndex = value;
                }
            }


            public NodeTypes Node
            {
                get 
                {
                    return node;
                }
                set 
                {
                    node = value;
                }
            }
            //Returns the TextValue of the current node
            public String TextValue
            {
                get 
                {
                    switch(Node)
                    {
                        case NodeTypes.Root :
                            return null;
                        case NodeTypes.Element : 
                            return null;
                        case NodeTypes.Attribute :
                            if( ElementType == 0 )
                            {
                                DirectoryInfo dInfo = (DirectoryInfo ) Doc;
                                switch(Attribute)
                                {
                                        case 0: return dInfo.Name;
                                        case 1: return dInfo.CreationTime.ToString();    
                                    }
                                }
                            else if( ElementType == 1 )
                            {
                                FileInfo fInfo = (FileInfo ) Doc;
                                switch(Attribute)
                                {
                                    case 0: return fInfo.Name;
                                    case 1: return (String) fInfo.CreationTime.ToString();
                                    case 2: return (String) fInfo.Length.ToString();
                                }
                            }
                            break;
                        case NodeTypes.Text :
                            return null;
                    }
                    return null;
                }
            }
            //returns the value of the attribute
            public String AttributeText
            {
                get 
                {
                    if( Node == NodeTypes.Attribute )
                        return AttributeIds[ElementType][Attribute];
                    return null;
                }
            }
            //Returns the name of the element.
            public String ElementText
            {
                get 
                {
                    return doc.Name;
                }
            }

        }
    }

請參閱

.NET Framework 中的 XPathNavigator