Condividi tramite


XPathNavigator su archivi diversi

XPathNavigator agevola l'esecuzione di query XPath su qualsiasi archivio che implementa l'interfaccia IXPathNavigable. Le tre classi che implementano l'interfaccia IXPathNavigable sono XPathDocument, XmlDocument e XmlDataDocument.

La classe XPathDocument consente l'esecuzione di query XPath su flussi di XML. È una classe altamente ottimizzata per l'esecuzione di query XPath e trasformazioni XSLT. Si tratta della classe preferita per le trasformazioni XSLT quando le prestazioni XPath sono la priorità principale.

La classe XmlDocument, oltre a fornire una visualizzazione modificabile dell'XML nel DOM, consente anche l'uso di XPath sui dati. XPathDocument e XmlDocument hanno un comportamento equivalente in termini dei risultati delle query sullo stesso XML. XmlDocument ha prestazioni inferiori rispetto a XPathDocument per le query XPath a causa delle capacità di modifica di questa classe.

La classe XmlDataDocument fornisce un DOM completamente conforme sincronizzato con un DataSet. Questa classe eredita dalla classe XmlDocument e consente di ottenere una visualizzazione XML gerarchica dei dati relazionali. Può essere utilizzata per eseguire query XPath sulla rappresentazione XML dei dati memorizzati in un dataset. Per ulteriori informazioni su XmlDataDocument, vedere Sincronizzazione di un DataSet con un XmlDataDocument

Per eseguire le query XPath su una qualsiasi di queste classi, richiamare la funzione CreateNavigator per restituire un XPathNavigator. L'XPathNavigator può quindi essere utilizzato per eseguire le query XPath su un documento, secondo la descrizione fornita nella parte restante di questa sezione.

È inoltre possibile estendere la classe XPathNavigator per definire i propri strumenti di selezione. Questo richiede la creazione di una struttura di nodi virtuale e l'implementazione di metodi per esplorare questa struttura. Una volta scritta la struttura dei nodi, viene attivata la possibilità di eseguire query XPath. I metodi Select nella classe XPathNavigator vengono implementati e necessitano solo di questa struttura per funzionare. Nell'esempio seguente viene implementato un XPathNavigator su un file system.

Cominciare con la creazione della struttura virtuale dei nodi. Si tratta di una struttura che espone il file system sottostante in modo che contenga tutte le informazioni nell'Infoset XML. Questa struttura di XML viene esposta tramite l'override dei metodi nella classe XPathNavigator che restituiscono le informazioni relative ai nodi. In questo esempio, le directory e i file verranno esposti come elementi. La struttura del file system verrà rispecchiata nella struttura dell'infoset XML esposto. Gli elementi che rappresentano le directory avranno come attributi il nome locale e l'ora di creazione della directory. Gli elementi che rappresentano i file avranno come attributi il nome locale, l'ora di creazione e la lunghezza del file. È possibile eseguire query su questo file system come /abc/xml che seleziona tutte le directory o i file con il nome locale xml, contenuti nelle directory con il nome locale abc.

Per esporre questa struttura, è necessario eseguire l'override delle proprietà seguenti dei nodi.

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

Per esporre questa struttura, è necessario eseguire l'override delle funzioni di accesso agli attributi elencati di seguito.

  • HasAttributes
  • GetAttributes
  • MoveToAttributes
  • MoveToFirstAttributes
  • MoveToNextAttributes

Per esporre questa struttura, è necessario eseguire l'override delle funzioni di accesso agli spazi dei nomi elencati di seguito.

  • GetNamespace
  • MoveToNamespace
  • MoveToFirstNamespace
  • MoveToNextNamespace

Utilizzando questi metodi e proprietà, è possibile esporre completamente la struttura sottostante come un Infoset XML.

Inoltre, tutte le stringhe LocalName, NameSpaceUri e Prefix devono essere aggiunte a una NameTable, fornita dalla proprietà NameTable. Quando le proprietà LocalName, NamespaceURI e Prefix vengono restituite, la stringa restituita dovrebbe provenire dalla NameTable. I confronti tra i nomi vengono eseguiti tramite confronto degli oggetti anziché delle stringhe, che sono significativamente più lente.

Inoltre, i metodi per l'esplorazione di questa struttura virtuale di nodi devono anch'essi essere implementati. Sono inclusi:

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

Questi metodi espongono la struttura sottostante come tale. Una volta implementati questi metodi e proprietà, tutte le query XPath funzionano su questo gruppo di nodi virtuale. Il codice riportato di seguito crea ad esempio un elemento XPathNavigator personalizzato per l'esplorazione delle directory di un disco rigido. L'esplorazione inizia da C:\Program Files e il programma analizza i file e le cartelle in modo ricorsivo. In alcuni computer questa operazione comporta la produzione di una grossa quantità di informazioni di output da inviare alla console. È possibile modificare il codice in modo da impostare come punto di partenza qualunque cartella del disco rigido.

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

        }
    }

Vedere anche

XPathNavigator in .NET Framework