Visualizzazione dei risultati da 1 a 5 su 5

Discussione: C# e file XML

  1. #1

    C# e file XML

    Ciao a tutti,
    ho cercato tanto in rete ma non ho trovato molto di quello che devo fare io.
    Vi spiego il problema: ho un file xml fatto in questo modo:
    codice:
    <nodeID="1"query="query 1"dbQuery="aaa">
        <nodeID="2"query="query 2"dbQuery="bbb"></node>
        <nodeID="3"query="query 3"dbQuery="ccc"></node>
        <nodeID="4"query="query 4"dbQuery="ddd"></node>
        <nodeID="5"query="query 5"dbQuery="eee">
             <nodeID="6"query="query 6"dbQuery="fff"></node>
        </node>
    </node>
    i sotto nodi possono essere di più o di meno ma mai si va oltre il secondo livello.
    Questo file xml lo leggo regolarmente con la funzione:

    codice:
    privatevoidProcessaSottoNodi(XmlNode node){
        if(node.HasChildNodes)
        {
            foreach(XmlNode cNode in node.ChildNodes)
            {
                Console.WriteLine("{0}", cNode.Name);
                foreach(XmlAttribute attr in cNode.Attributes)
                   Console.WriteLine("{0}", attr.Name+" = "+ attr.InnerText);
    
                Console.WriteLine("-------------------------------");
            ProcessaSottoNodi(cNode);
            }
        } 
    }
    Però quando provo a modificare il file xml aggiuggendo TextB, TextC, TextF:
    codice:
    <nodeID="1"query="query 1"dbQuery="aaa">
        <nodeID="2"query="query 2"dbQuery="bbb">TextB</node>
        <nodeID="3"query="query 3"dbQuery="ccc">TextC</node>
        <nodeID="4"query="query 4"dbQuery="ddd">TextD</node>
        <nodeID="5"query="query 5"dbQuery="eee">
             <nodeID="6"query="query 6"dbQuery="fff">TextF</node>
        </node>
    </node>
    La funzione mentre processa i nodi va in errore nel secondo foreach, perché ??
    aggiungendo tra i due foreach il codice:
    codice:
    Console.WriteLine("{0}", cNode.Name + " = " + cNode.InnerText);
    Mi compare "node = TextBTextCTextDTextF" e poi un "#text" e si ferma tutto.
    ho provato a cerca nella grande rete ma non sono riuscito a trovare risposta.
    o trovo delle semplici funzioni per poter trovare l'innertext o trovo metodi per leggere gli attributi degli elementi.

    Grazie mille

  2. #2
    Utente di HTML.it
    Registrato dal
    Jul 2015
    Messaggi
    57
    il problema risiede nella lettura degli attributi

    codice:
    <node nodeID="2"query="query 2"dbQuery="bbb">TextB</node>
    in questo caso XmlNode è rappresentato da 'node' in seguito leggi la lista di attributi (nodeID, query, dbQuery)

    ricorsivamente chiedi a questo 'node' se ha figli, e lui risponde di si: TextB è un suo figlio.

    quindi XmlNode è rappresentato da 'TextB' in seguito leggi la lista di attributi (nodeID, query, dbQuery) però un attimo, TextB non ha attributi, pertanto:

    codice:
      foreach(XmlAttribute attr in cNode.Attributes)
    cNode.Attributes ti da nullo di consequenza avviende l'interruzione per oggetto non impostato su un istanza di un oggetto.

    quindi per farlo andare ti basta verificare che Attributes non sia nullo e se lo è fare un continue.

    codice:
    privatevoidProcessaSottoNodi(XmlNode node){
        if(node.HasChildNodes)
        {
            foreach(XmlNode cNode in node.ChildNodes)
            {
      if(cNode.Attributes== null)
       continue;
    
                Console.WriteLine("{0}", cNode.Name);
                foreach(XmlAttribute attr in cNode.Attributes)
                   Console.WriteLine("{0}", attr.Name+" = "+ attr.InnerText);
    
                Console.WriteLine("-------------------------------");
            ProcessaSottoNodi(cNode);
            }
        } 
    }
    Questo ti risolve il problema, però devi chiederti se ha senso avere un Xml del genere, di sicuro preferisci avere un file che puoi sempre leggere in qualsiasi situazione, senza dover mettere uno, due o infiniti "se" per tappare mancanze.

    Quindi per sistemare in modo definitivo il problema gli elementi TextA, B, etc o vengono messi come attributi, oppure quello che ti consiglio è cambiare totalmente la gestione.

    cioè usare l'XmlSerializer
    è una classe apposta creata che ti permette Reflection tra il tuo file ed un oggetto stilato apposta, legge veloce preciso, chiaramente è pignolo
    questo ti impone una struttura da seguire, pretende che i dati siano corretti.

    Ammettendo un Xml output o input del genere:
    codice:
      <node nodeID="1" query="select l from a" dbQuery="select l from f">
        <node nodeID="2" query="select l from a" dbQuery="select l from f">
          <text>TextA</text>
        </node>
        <node nodeID="3" query="select l from a" dbQuery="select l from f">
          <text>TextA</text>
          <node nodeID="4" query="select l from a" dbQuery="select l from f">
            <text>TextA</text>
          </node>
          <node nodeID="5" query="select l from a" dbQuery="select l from f">
            <text>TextA</text>
          </node>
        </node>
      </node>
      <node nodeID="6" query="select l from a" dbQuery="select l from f" />
      <node nodeID="7" query="select l from a" dbQuery="select l from f" />
      <node nodeID="8" query="select l from a" dbQuery="select l from f">
    ogni nodo può avere figli nodi, può avere figli nodi ed un elemento testo o solo elemento testo.

    Il tuo oggetto C# che si interfaccia alla struttura desiderata.
    codice:
        /// <summary>
        /// Fornisce una struttura per la riflessione con un file Xml.
        /// </summary>
        [XmlRoot("node")]
        public class Node
        {
            /// <summary>
            /// Ottiene o setta l'id.
            /// </summary>
            [XmlAttribute("nodeID")]
            public int NodeID { get; set; }
    
            /// <summary>
            /// Ottiene o setta la query.
            /// </summary>
            [XmlAttribute("query")]
            public string Query { get; set; }
    
            /// <summary>
            /// Ottiene o setta la query del db.
            /// </summary>
            [XmlAttribute("dbQuery")]
            public string DBQuery { get; set; }
    
            /// <summary>
            /// Ottiene o setta il testo.
            /// </summary>
            [XmlElement("text")]
            public string Text { get; set; }
    
            /// <summary>
            /// Ottiene o setta i nodi figli.
            /// </summary>
            [XmlElement("node")]
            public List<Node> Children { get; set; }
    
            /// <summary>
            /// Costruttore.
            /// </summary>
            public Node() {  Children = new List<Node>(); }
    
            /// <summary>
            /// Returns a string that represents the current object.
            /// </summary>
            /// <returns>A string that represents the current object.</returns>
            public override string ToString()
            {
                return String.Format("Node Id: {0}, Query: {1}, DBQuery: {2}, Nr. children: {3}",
                    NodeID, Query, DBQuery, Children.Count);
            }
        }
    il lettore del file xml.
    codice:
            public static object DeserializeXml(string fullFileName, Type type)
            {
                XmlSerializer serializer = new XmlSerializer(type);
                using (FileStream stream = new FileStream(fullFileName, FileMode.Open))
                    return serializer.Deserialize(stream);
            }
    Ultima modifica di Marsh; 21-08-2015 a 14:23

  3. #3
    Wow, grazie mille per una risposta così completa, non me l'aspettavo proprio.
    Allora... se fosse per me organizzerei l'Xml in maniera totalmente diversa, il problema è che io ho uno strumento di misure elettriche che mi produce a fine controllo un file xml (diverso in base al controllo effettuato) del piffero e la cosa peggio è che il software dello strumento è ancora peggio. Quindi la mia idea era quella di sviluppare un programmino che mi potesse leggere l'Xml e ri-strutturare per poterlo salvare in un db.
    Adesso mi sto documentato sul serialize/deserialize e con un potevole lavoro (sbattimento) potrei creare delle classi per il mio file (parliamo di un xml di soltanto 473 righe).
    Ho però una domanda, se avessi dei nodi fatti in questo modo:

    codice:
    <node ID="1">
       <node ID="2">
         <text>TextA</text>
       </node>
       <node ID="3">
         <text>TextA</text>
         <node ID="4" Desc="Descrizione">
            <text>TextA</text>
         </node>
         <node ID="5">
            <text>TextA</text>
         </node>
       </node>
    </node>
    

    Il deserialize come si comporta quando definisco un attributo e magari non lo trova nell'elemento ??

  4. #4
    Utente di HTML.it
    Registrato dal
    Jul 2015
    Messaggi
    57
    ogni file Xml se scritto giusto impone una radice pertanto nel tuo caso è cosi strutturato:
    codice:
    <?xml version="1.0" encoding="utf-8"?>
    <node ID="1"> // radice
      <node ID="2"> // figlio 1 id 1
        <text>TextA</text> // figlio 1 id 2
      </node>
      <node ID="3"> // figlio 2 id 1
        <text>TextA</text> // figlio 1 id 3
        <node ID="4" Desc="Descrizione"> // figlio 2 id 3
          <text>TextA</text> // figlio 1 id 4
        </node>
        <node ID="5"> // figlio 3 id 3
          <text>TextA</text> // figlio 1 id 5
        </node>
      </node>
    </node>

    quindi il tuo oggetto che verrà associato all'xml seguente necessita di:
    • - la radice, rappresentata da un nodo (Node)[
      • [XmlRoot("node")]

    • - un attributo di tipo ID (int) per ogni nodo
      • [XmlAttribute("ID")]

    • - un attributo di tipo Desc (string) per ogni nodo
      • [XmlAttribute("Desc")]

    • - un elemento di tipo text (string) per ogni nodo
      • [XmlElement("text")]

    • - una ricorsività di elementi nodi essendo che ogni nodo può contenere altri nodi (List<Node>), per ogni nodo
      • [XmlElement("node")]



    se non viene definito un tag di tipo Xml nell'oggetto c#, verrà cercato sempre come elemento con il nome della proprietà, questo significa:
    codice:
            /// <summary>
            /// Ottiene o setta il testo.
            /// </summary>
            [XmlElement("text")]
            public string Text1 { get; set; }
    
            /// <summary>
            /// Ottiene o setta il testo.
            /// </summary>
            public string Text2 { get; set; }
    la prima proprietà verrà cercata nel file Xml con il tag elemento text (<text/>).
    la seconda proprietà verrà cercata nel file xml con il tag elemento Text2 (<Text2/>).

    questo significa che scrivendo il seguente oggetto c# in Xml verranno scritte tutte le proprietà dell'oggetto stesso, per escludere la scrittura di alcune puoi usare il tag [XmlIgnore].

    L'unica cosa che devi rispettare assolutamente è il tipo di dati, il tag <text> che contiene una stringa, nel file c# dovrà essere di tipo stringa.

    per rispondere alla tua domanda, non succede nulla.
    se nel file Xml sono presenti più campi dell'oggetto associato, i campi in più non saranno ne letti ne scritti.
    se nell'oggetto c# sono presenti più proprietà del file Xml, le proprietà in più non verranno lette, però verranno scritte, se non sono taggate [XmlIgnore].

    Pertanto la tua classe c# che si rifletterà sull'Xml sarà strutturato nel modo seguente, sia per la lettura che per l'eventuale scrittura:
    codice:
        /// <summary>
        /// Fornisce una struttura per la riflessione con file Xml.
        /// </summary>
        [XmlRoot("node")]
        public class Node
        {
            /// <summary>
            /// Ottiene o setta l'id.
            /// </summary>
            [XmlAttribute("ID")]
            public int ID { get; set; }
    
            /// <summary>
            /// Ottiene o setta la descrizione.
            /// </summary>
            [XmlAttribute("Desc")]
            public string Description { get; set; }
    
            /// <summary>
            /// Ottiene o setta il testo.
            /// </summary>
            [XmlElement("text")]
            public string Text { get; set; }
    
            /// <summary>
            /// /// Ottiene o setta i nodi figli.
            /// </summary>
            [XmlElement("node")]
            public List<Node> Children { get; set; }
    
            /// <summary>
            /// Costruttore.
            /// </summary>
            public Node() { Children = new List<Node>(); }
    
            /// <summary>
            /// Ottiene un valore di nullità in formato stringa per i valori nulli.
            /// </summary>
            /// <param name="s">Valore da verificare se è nullo.</param>
            /// <returns>Ritorna o.ToString() se non è nullo, altrimenti "{x:Null}".</returns>
            public static string StringIfIsNull(object o)
            {
                return o == null ? "{x:Null}" : o.ToString();
            }
    
            /// <summary>
            /// Returns a string that represents the current object.
            /// </summary>
            /// <returns>A string that represents the current object.</returns>
            public override string ToString()
            {
                return String.Format("Node Id: {0}, Text: {1}, Description {2}, Nr. children: {3}",
                    ID,
                    Node.StringIfIsNull(Text),
                    Node.StringIfIsNull(Description),
                    Children.Count);
            }
        }
    Metodo completo per leggere con XmlSerializer
    codice:
            /// <summary>
            /// Legge un file XML.
            /// </summary>
            /// <param name="fullFileName">Percorso di locazione del file.</param>
            /// <param name="type">Tipo oggetto Xml da leggere.</param>
            /// <returns>Ritorna l'oggetto XML del file di tipo 'type'.</returns>
            public static object Deserialize(string fullFileName, Type type)
            {
                XmlSerializer serializer = null;
                Stream reader = null;
                try
                {
                    serializer = new XmlSerializer(type);
                    reader = new FileStream(fullFileName, FileMode.Open);
                    return serializer.Deserialize(reader);
                }
                catch
                {
                    throw;
                }
                finally
                {
                    if (reader != null)
                    {
                        try { reader.Close(); }
                        catch { }
                    }
                }
            }
    Esempio d'utilizzo
    codice:
        public class Program
        {
            private static readonly string XmlFullFileName = @"C:\data.xml";
    
            public static void Main(string[] args)
            {
                Node root = (Node)XmlSerializerProcessor.Deserialize(Program.XmlFullFileName, typeof(Node));
                Program.PrintNode(root);
                Console.ReadLine();
            }
    
            private static void PrintNode(Node node)
            {
                Console.WriteLine(node.ToString());
                for (int i = 0; i < node.Children.Count; i++)
                    Program.PrintNode(node.Children[i]);
            }
        }

    importante notare una cosa: le proprietà devono essere pubbliche sia per il getter che per il setter nel file associativo c#.
    Ultima modifica di Marsh; 24-08-2015 a 11:31

  5. #5
    Provato e va tutto bene!!
    Grazie mille.... adesso mi aspetta il duro lavoro di creare l'immensa classe per i miei Xml..
    Ultima modifica di fabio.battaglia; 24-08-2015 a 15:52

Tag per questa discussione

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.