Compartir a través de


XPathNavigator sobre diferentes almacenes

XPathNavigator permite realizar consultas de XPath sobre un almacén que implemente la interfaz IXPathNavigable. Las tres clases que implementan la interfaz IXPathNavigable son XPathDocument, XmlDocument y XmlDataDocument.

La clase XPathDocument permite efectuar consultas de XPath sobre secuencias de XML. Se ha optimizado en gran medida para realizar consultas de XPath y transformaciones XSLT. Ésta es la clase preferida para las transformaciones XSLT cuando la prioridad es obtener el máximo rendimiento de XPath.

La clase XmlDocument, además de proporcionar una vista DOM modificable del código XML, también posibilita el uso de XPath en los datos. Las clases XPathDocument y XmlDocument demuestran un comportamiento equivalente en lo que se refiere a los resultados de las consultas sobre el mismo XML. La clase XmlDocument es menos eficaz que XPathDocument para las consultas de XPath debido a las capacidades de edición de esta clase.

La clase XmlDataDocument proporciona un DOM totalmente compatible que se sincroniza con un Dataset. Hereda de la clase XmlDocument y se utiliza para obtener una vista XML jerárquica de datos relacionales. Se puede utilizar para realizar consultas XPath en la representación XML de los datos almacenados en un Dataset. Para obtener más información acerca de la clase XmlDataDocument, vea Sincronizar DataSet con XmlDataDocument.

Con el fin de efectuar consultas de XPath en alguna de estas clases, invoque a la función CreateNavigator para que devuelva un XPathNavigator. Entonces, el objeto XPathNavigator se puede usar para realizar consultas de XPath en el documento, según se describe en el resto de esta sección.

También es posible extender la clase XPathNavigator para definir sus propios exploradores. Esto requiere la creación de una estructura de nodos virtuales y la implementación de métodos para desplazarse por ella. Una vez creada esta estructura de nodos, se habilita la capacidad de realizar consultas de XPath. Los métodos Select de la clase XPathNavigator están implementados y sólo necesitan esta estructura para funcionar. En el ejemplo siguiente se implementa una clase XPathNavigator sobre un sistema de archivos.

Para comenzar, se crea la estructura de nodos virtuales. Se trata de una estructura que expone el sistema de archivos subyacente para contener toda la información del conjunto de información de XML. Para exponer esta estructura de XML, se reemplazan los métodos de la clase XPathNavigator que devuelven información acerca de los nodos. En este ejemplo, los directorios y archivos se expondrán como elementos. La estructura de árbol del sistema de archivos se reflejará en la estructura de árbol del conjunto de información de XML expuesto. Los elementos que representan directorios tendrán como atributos el nombre local y la fecha de creación del directorio. Los elementos que representan archivos tendrán como atributos el nombre local, la fecha de creación y la longitud del archivo. En este sistema de archivos puede realizar consultas como /abc/xml, que selecciona todos los directorios o archivos con el nombre local xml que están contenidos en los directorios con el nombre local abc.

Con el fin de exponer esta estructura, se deben reemplazar las propiedades siguientes de los nodos.

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

Con el fin de exponer esta estructura, se deben reemplazar los descriptores de acceso a atributos siguientes.

  • HasAttributes
  • GetAttributes
  • MoveToAttributes
  • MoveToFirstAttributes
  • MoveToNextAttributes

También se deben reemplazar los descriptores de acceso a espacios de nombres siguientes.

  • GetNamespace
  • MoveToNamespace
  • MoveToFirstNamespace
  • MoveToNextNamespace

Mediante estos métodos y propiedades es posible exponer completamente la estructura subyacente como un conjunto de información de XML.

Además, se deben agregar todas las cadenas LocalName, NameSpaceUri y Prefix a una NameTable, proporcionada por la propiedad NameTable. Cuando se devuelven las propiedades LocalName, NamespaceURI y Prefix, la cadena devuelta también proviene de la NameTable. Las comparaciones entre los nombres se efectúan entre objetos en lugar de entre cadenas, lo que es significativamente más lento.

Además, también se deben implementar métodos para desplazarse a través de esta estructura de nodos virtuales. Entre éstos se incluyen:

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

Estos métodos exponen la estructura subyacente en forma de árbol. Después de implementar estos métodos y propiedades, todas las consultas de XPath funcionan en este conjunto de nodos virtuales. Por ejemplo, en el código siguiente se crea un XPathNavigator personalizado que se desplaza a través del directorio en un disco duro. El programa tiene como punto inicial de desplazamiento C:\Program Files, y se mueve de forma recursiva a través de archivos y carpetas, lo que en el caso de algunos equipos, implica enviar gran cantidad de salida a la consola. Se puede modificar el código para empezar en cualquier carpeta que exista en el disco duro.

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

        }
    }

Vea también

XPathNavigator en .NET Framework