none
Como guardar datos de un xml con consultas XPath en una tabla de Sql server RRS feed

  • Pregunta

  • Buen día, tengo la siguiente pregunta

    Espero por favor puedan ayudarme

    Realizo un programa que tenga que guardar datos de un xml en una base de datos sql, para localizarlos posteriormente los busco mediante una consulta xpath en el xml

    Mi problema es a la hora de extraer ese dato, como es que debería seleccionarlo de la consulta, se debe cargar en una variable?

    si es así como seria la estructura?

    Consulta: 

     XmlNodeList xnodel1 = xDoc.SelectNodes("xmlns:nodo1/dato",xmlmanager);

    //cual seria el codigo para extraer ese dato de la consulta de arriba?

    //Como estructurar la manera en que lo guarde en una tabla de sql server?

    He probado varias soluciones del foro pero no te tenido resultados

    Soy recién principiante 

    GRACIAS!

     

    alex1001

    miércoles, 27 de diciembre de 2017 16:12

Respuestas

  • Tienes mal el XPath. "Datos/campo2" te buscaría un ELEMENTO campo2 por debajo del elemento Datos. Pero tu campo2 no es un elemento sino un ATRIBUTO. Puedes buscar Datos y luego separar su atributo:

    XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);  
    
    foreach (XmlNode nodo in xnodel1)
    {
        string campo2 = nodo.Attributes["campo2"].Value;
        Console.WriteLine(campo2);
    }
    

    jueves, 28 de diciembre de 2017 10:36
    Moderador
  • Si estás usando SQL Server, y es una versión razonablemente moderna, puedes declarar un campo de la taba con el tipo XML (en lugar de varchar o similar). La ventaja de declararlo como XML es que entiende directamente las consultas XPATH, que se pueden embeber dentro de una consulta SQL. Incluso se pueden crear índices XML para acelerar estas consultas. Por lo tanto, no hace falta que cargues el dato en una variable para luego aplicarle el XPATH desde C# (cosa que requeriría recorrer todos los registros de la tabla para examinarlos uno a uno ejecutando la consulta XPATH en cliente).

    Un ejemplo del tipo de consulta que puedes enviar al servidor desde tu código cliente:

    SELECT Instructions.query('declare namespace AWMI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";           
        /AWMI:root/AWMI:Location[@LocationID=10]  
    ') as Result   
    FROM  Production.ProductModel  
    WHERE ProductModelID=7  

    Te he copiado aposta un ejemplo complicado que declara un namespace antes del XPATH porque he visto que en tu código en C# estabas usando namespaces.

    Documentación:

    https://docs.microsoft.com/es-es/sql/xquery/xquery-language-reference-sql-server

    • Propuesto como respuesta Pablo RubioModerator jueves, 28 de diciembre de 2017 17:23
    • Marcado como respuesta Carlos Atm jueves, 28 de diciembre de 2017 23:48
    miércoles, 27 de diciembre de 2017 18:35
    Moderador
  • Para recorrer los nodos:

    foreach (XmlNode nodo in xnodel1) ...

    Cada uno de los valores de "nodo" contiene un nodo completo de los que buscaste. Si es un nodo simple que no tiene dentro otros nodos anidados, puedes usar nodo.InnerXml para tomar el texto interno. Si es más complejo, puedes seguir usando SelectNodes o SelectSingleNode con más XPATH para "digerir" de la misma manera el contenido de "nodo".

    Una vez que tengas el InnerXml, es una simple cadena de texto, puedes mandarlo a grabar en la base de datos igual que cualquier otro texto, olvidándote ya de que proviene de un XML.

    • Propuesto como respuesta Pablo RubioModerator miércoles, 27 de diciembre de 2017 22:45
    • Marcado como respuesta Carlos Atm jueves, 28 de diciembre de 2017 23:48
    miércoles, 27 de diciembre de 2017 20:19
    Moderador
  • Si quieres hacer la inserción con un SP, se haría de forma muy parecida a como la tienes con el INSERT, es decir, creando un SqlCommand, poniéndole el nombre del procedimiento (en lugar de la sentencia INSERT), y añadiéndole los parámetros de la misma manera (por cierto, te falta la @ en el parámetro). La única diferencia es que se le pone Cmd.CommandType = CommandType.StoredProcedure.

    Lo del "Console.Writeline" ya puedes borrarlo, solo era un ejemplo de como "hacer algo" con el dato encontrado.

    La grabación tendrías que meterla dentro del foreach (que es donde tenemos el dato que as a grabar), no fuera del bucle (donde esa variable no es visible). La variable es la misma que estábamos escribiendo en el Console.Writeline, no el xnodel1 que estás usando.

    Por lo demás, en principio el mecanismo que estás usando es correcto. Acuérdate de cerrar la conexión.


     XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);
     sqlConnection1.Open();
     foreach (XmlNode nodo in xnodel1)
     {
         string campo2 = nodo.Attributes["campo2"].Value;
         string query = "INSERT INTO Tabla1(campo2) VALUES (@campo2)";  
         SqlCommand Cmd = new SqlCommand(query, sqlConnection1);
         Cmd.Parameters.AddWithValue("@campo2", campo2);
         Cmd.ExecuteNonQuery();
     } 
     sqlConnection1.Close();
    
    

    jueves, 28 de diciembre de 2017 21:34
    Moderador
  • Se tendría que hacer las 24 consultas xpath [...]

    DEPENDE. Si son 24 elementos completamente distintos desperdigados por el XML sí que tendrías que hacer las 24 consultas. Pero si son atributos del mismo elemento, entonces basta con usar una sola consulta XPATH para encontrar el elemento, y luego acceder a los 24 atributos de ese elemento. Te pongo un ejemplo con dos atributos, no tienes más que añadir los 22 restantes :-)

     XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);
     sqlConnection1.Open();
     foreach (XmlNode nodo in xnodel1)
     {
         string query = "INSERT INTO Tabla1(campo1, campo2) VALUES (@campo1, @campo2)";  
         SqlCommand Cmd = new SqlCommand(query, sqlConnection1);
         Cmd.Parameters.AddWithValue("@campo1", nodo.Attributes["campo1"].Value);
         Cmd.Parameters.AddWithValue("@campo2", nodo.Attributes["campo2"].Value);
         Cmd.ExecuteNonQuery();
     } 
     sqlConnection1.Close();

    • Marcado como respuesta Carlos Atm viernes, 29 de diciembre de 2017 17:13
    viernes, 29 de diciembre de 2017 11:03
    Moderador

Todas las respuestas

  • Si estás usando SQL Server, y es una versión razonablemente moderna, puedes declarar un campo de la taba con el tipo XML (en lugar de varchar o similar). La ventaja de declararlo como XML es que entiende directamente las consultas XPATH, que se pueden embeber dentro de una consulta SQL. Incluso se pueden crear índices XML para acelerar estas consultas. Por lo tanto, no hace falta que cargues el dato en una variable para luego aplicarle el XPATH desde C# (cosa que requeriría recorrer todos los registros de la tabla para examinarlos uno a uno ejecutando la consulta XPATH en cliente).

    Un ejemplo del tipo de consulta que puedes enviar al servidor desde tu código cliente:

    SELECT Instructions.query('declare namespace AWMI="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";           
        /AWMI:root/AWMI:Location[@LocationID=10]  
    ') as Result   
    FROM  Production.ProductModel  
    WHERE ProductModelID=7  

    Te he copiado aposta un ejemplo complicado que declara un namespace antes del XPATH porque he visto que en tu código en C# estabas usando namespaces.

    Documentación:

    https://docs.microsoft.com/es-es/sql/xquery/xquery-language-reference-sql-server

    • Propuesto como respuesta Pablo RubioModerator jueves, 28 de diciembre de 2017 17:23
    • Marcado como respuesta Carlos Atm jueves, 28 de diciembre de 2017 23:48
    miércoles, 27 de diciembre de 2017 18:35
    Moderador
  • Gracias por responder.

    Ahora por el momento es con Sql Server 2008 necesariamente.

    Perdón me explique mal, No quiero extraer los datos de una tabla de sql server,

    lo que necesito es:  extraer datos de un archivo xml que se encuentra en mi equipo para después mandarlo a una tabla de un base de datos de sql server.

    Por eso mismo ocupo las consultas xpath, lo que no se es que sigue después de tener la consulta?

    XmlNodeList xnodel1 = xDoc.SelectNodes("xmlns:nodo1/dato",xmlmanager);

    //como es que adquiero el resultado de la consulta?

    // y como es que deberia estructurar el codigo para mandarlo a la tabla de la base de datos 

    De ante mano gracias 



    alex1001

    miércoles, 27 de diciembre de 2017 19:40
  • Para recorrer los nodos:

    foreach (XmlNode nodo in xnodel1) ...

    Cada uno de los valores de "nodo" contiene un nodo completo de los que buscaste. Si es un nodo simple que no tiene dentro otros nodos anidados, puedes usar nodo.InnerXml para tomar el texto interno. Si es más complejo, puedes seguir usando SelectNodes o SelectSingleNode con más XPATH para "digerir" de la misma manera el contenido de "nodo".

    Una vez que tengas el InnerXml, es una simple cadena de texto, puedes mandarlo a grabar en la base de datos igual que cualquier otro texto, olvidándote ya de que proviene de un XML.

    • Propuesto como respuesta Pablo RubioModerator miércoles, 27 de diciembre de 2017 22:45
    • Marcado como respuesta Carlos Atm jueves, 28 de diciembre de 2017 23:48
    miércoles, 27 de diciembre de 2017 20:19
    Moderador
  • Hola Alberto, gracias por responder

    Espero por favor puedas explicarme en relación a mi código:

    Este seria mi xml:

    <tolt:Datos campo1="valor1" campo2= "valor2"

    </tolt:Datos>

    Mi consulta seria para extraer valor2:  XmlNodeList xnodesl1 = xDoc.SelectNodes("tolt:Datos/campo2", xmlmanager);  

    foreach (XmlNode nodo in xnodel1)
                {

    //como seria el codigo para que lo extragera
                }

    Tengo este codigo pero quiero que funcione para lo de arriba: 

    Console.WriteLine();
    for(int i = 0; i < xnodesl1.Count; i++)
    {
    Console.WriteLine(xnodesl1[i].InnerText);
    }

    Espero puedas ayudarme con la sentencia para guardar ese dato en una tabla Saludos!


    alex1001

    jueves, 28 de diciembre de 2017 0:02
  • Tienes mal el XPath. "Datos/campo2" te buscaría un ELEMENTO campo2 por debajo del elemento Datos. Pero tu campo2 no es un elemento sino un ATRIBUTO. Puedes buscar Datos y luego separar su atributo:

    XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);  
    
    foreach (XmlNode nodo in xnodel1)
    {
        string campo2 = nodo.Attributes["campo2"].Value;
        Console.WriteLine(campo2);
    }
    

    jueves, 28 de diciembre de 2017 10:36
    Moderador
  • Gracias Alberto por la explicacion ya comprendo mas el tema de las consultas xphat.

    Para pasar ese valor que ya se guardo en la variable string a una base de datos como se haria la inseccion con un store prodeduce?

    Tengo este codigo que tengo pero me arroga este error: System.ArgumentException: 'No hay ninguna asignación de tipo de objeto System.Xml.XPathNodeList a un tipo nativo de un proveedor administrado conocido.'

                         

    XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);

     foreach (XmlNode nodo in xnodel1)
                    {
                        string campo2 = nodo.Attributes["campo2"].Value;
                        Console.WriteLine(campo2);
                    }

     sqlConnection1.Open();
    string query = "INSERT INTO Tabla1 ( campo2) VALUES (campo2)";  
    SqlCommand Cmd = new SqlCommand(query, sqlConnection1);
    Cmd.Parameters.AddWithValue("campo2", Convert.ToString(xnodel1));

    Estaría bien la estructura tengo la duda si para mandar los datos a la tabla estén bien, obviamente simplifique ya que de un xml debo extraer mas de 20 datos 

    Me ayudarías en estructurar para mandarlo a la base de datos o caul seria una forma mas sencilla de mandar los datos a sql

    De ante mano muchas gracias


    alex1001

    jueves, 28 de diciembre de 2017 20:19
  • Si quieres hacer la inserción con un SP, se haría de forma muy parecida a como la tienes con el INSERT, es decir, creando un SqlCommand, poniéndole el nombre del procedimiento (en lugar de la sentencia INSERT), y añadiéndole los parámetros de la misma manera (por cierto, te falta la @ en el parámetro). La única diferencia es que se le pone Cmd.CommandType = CommandType.StoredProcedure.

    Lo del "Console.Writeline" ya puedes borrarlo, solo era un ejemplo de como "hacer algo" con el dato encontrado.

    La grabación tendrías que meterla dentro del foreach (que es donde tenemos el dato que as a grabar), no fuera del bucle (donde esa variable no es visible). La variable es la misma que estábamos escribiendo en el Console.Writeline, no el xnodel1 que estás usando.

    Por lo demás, en principio el mecanismo que estás usando es correcto. Acuérdate de cerrar la conexión.


     XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);
     sqlConnection1.Open();
     foreach (XmlNode nodo in xnodel1)
     {
         string campo2 = nodo.Attributes["campo2"].Value;
         string query = "INSERT INTO Tabla1(campo2) VALUES (@campo2)";  
         SqlCommand Cmd = new SqlCommand(query, sqlConnection1);
         Cmd.Parameters.AddWithValue("@campo2", campo2);
         Cmd.ExecuteNonQuery();
     } 
     sqlConnection1.Close();
    
    

    jueves, 28 de diciembre de 2017 21:34
    Moderador
  • Muchas gracias Alberto por la descripción, me ha servido de mucho.

    Tengo una ultima  duda como te comentaba que pasaría si tuviera que extraer un poco mas de 24 datos del xml, como  tendría que  codificar la estructura de manera que pudiera ingresar los 24 datos del xml?

    Se tendría que hacer las 24 consultas xpath, para después buscar en cada consulta el valor cierto?

    Pero como se haría a la hora de mandar los datos juntos de el resultado de todas las consultas xpath a la base de datos para que todo quedara en una fila en sus campos correspondientes de la tabla?

    Como se modificaría el foreanch (para cada consulta xpath o para leer  las 24 consultas en general con su respectivo SP)

    De ante mano muchas gracias por resolver mis dudas

    Saludos!


    alex1001

    jueves, 28 de diciembre de 2017 23:39
  • Se tendría que hacer las 24 consultas xpath [...]

    DEPENDE. Si son 24 elementos completamente distintos desperdigados por el XML sí que tendrías que hacer las 24 consultas. Pero si son atributos del mismo elemento, entonces basta con usar una sola consulta XPATH para encontrar el elemento, y luego acceder a los 24 atributos de ese elemento. Te pongo un ejemplo con dos atributos, no tienes más que añadir los 22 restantes :-)

     XmlNodeList xnodesl1 = xDoc.SelectNodes("//tolt:Datos", xmlmanager);
     sqlConnection1.Open();
     foreach (XmlNode nodo in xnodel1)
     {
         string query = "INSERT INTO Tabla1(campo1, campo2) VALUES (@campo1, @campo2)";  
         SqlCommand Cmd = new SqlCommand(query, sqlConnection1);
         Cmd.Parameters.AddWithValue("@campo1", nodo.Attributes["campo1"].Value);
         Cmd.Parameters.AddWithValue("@campo2", nodo.Attributes["campo2"].Value);
         Cmd.ExecuteNonQuery();
     } 
     sqlConnection1.Close();

    • Marcado como respuesta Carlos Atm viernes, 29 de diciembre de 2017 17:13
    viernes, 29 de diciembre de 2017 11:03
    Moderador
  • Buen día Alberto, muchas gracias por responder.

    Sobre los datos que necesito si están desperdigados en todo el xml.

    Entonces como le haría para mandar los datos a la tabla sql de cada consulta diferente Alberto? Como me indicantes anteriormente tendría que hacer la operación de guardar los datos en la tabla sql adentro del Foreanch para que guarde el dato de la consulta xpath.

    Pero en este caso de que son las 24 consultas de los elementos distintos separados del xml como quedaría con el INSERT  que utilizo  o  con un SP?

    Me podrías ayudar indicándome la forma correcta de mandar los datos de las 24 consultas con un ejemplo? ya que no se como hacer que esos datos se guarden en la tabla de manera conjunta en una sola fila

    De ante mano muchas gracias por la ayuda me ha servido de mucho.

    Saludos



    alex1001


    • Editado Carlos Atm viernes, 29 de diciembre de 2017 18:18
    viernes, 29 de diciembre de 2017 17:13
  • Para. Si están desperdigados, ¿sabes seguro que cada uno de ellos solo tiene una ocurrencia?

    De ser así, en lugar del SelectNodes deberías usar SelectSingleNode, y te sobra el foreach. Simplemente llámalo varias veces, tomando cada valor y guardándolo en una variable, y luego con todas esas variables las pasas a los parámetros del procedimiento como en los ejemplos anteriores.

    Y si no es así, y puede haber varios resultados en cada búsqueda, entonces ¿Cómo agrupas los distintos resultados de los distintos elementos para dar lugar a los varios registros que inserta el procedimiento almacenado? ¿Cuál es tu criterio para saber cuáles de los varios resultados se corresponden con cuáles de los otros para grabar?

    sábado, 30 de diciembre de 2017 10:36
    Moderador