XML: Aggiunta di child node con attributi a child node esistente

di il
8 risposte

XML: Aggiunta di child node con attributi a child node esistente

Ciao a tutti,

sto studiando XML e vorrei utilizzare la classe XmlDocument per fare un update di un file XML.
In particolare vorrei aggiungere un nodo (elemento <teacher>) all'interno di un altro nodo (elemento <Teachers>) che è a sua volta già child node dell'elemento <School> (root).

Tutti i child nodes <teacher> dentro <Teachers> hanno gli stessi attributi (Name, Surname, ecc.), quindi anche il mio nuovo elemento <teacher> avrà gli stessi attributi.

La struttura del Xml già esistente è questa:
<School>

          <Students>
             <student StudentID="1"   Name="Joanna"  year = "2">     </student >
             <student StudentID="2"   Name="Joanna"  year = " 3 ">   </student >   
         </Students>

        <Teachers>
              <teacher TeacherID = " "  Name= " " Surname= " " > </teacher >
              <teacher TeacherID = " "  Name= " " Surname= " " > </teacher >   
        </Teachers>

    </School>

Problema : aggiungere un terzo <teacher> sotto il secondo <teacher>, con gli stessi attributi e valorizzarli.

Quello che ho provato a fare io è questo:

                const string nomeFile = @"C:Users/nomeUser/Documents/School.xml";

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.Load(nomeFile);
                
                XmlNode Teachers= xmlDoc.SelectSingleNode("//Teachers");
                XmlNode lastTeacher = xmlDoc.SelectSingleNode("//teacher"); 
                XmlNode newTeacher = xmlDoc.CreateNode(XmlNodeType.Element, "teacher", null);
                
                xmlDoc.CreateAttribute("TeacherID ", oggettoTeacher.Id.ToString());
                xmlDoc.CreateAttribute("Name", oggettoTeacher.Name);                
                xmlDoc.CreateAttribute("Surname", oggettoTeacher.Surname);               
                
                Teachers.InsertAfter(newTeacher , lastTeacher );    // dico al parent node <Teachers> di inserire il nuovo child node [i]newTeacher [/i]dopo l'ultimo child node già presente che è [i]lastTeacher[/i].
                
                xmlDoc.Save(nomeFile);

Dato che non funziona, mi chiedevo se a essere sbagliato è qualcosa che riguarda il modo in cui definisco il path del file, se dovrei specificare qual è la root anziché istanziare oggetti XmlNode o se sto proprio sbagliando approccio...

8 Risposte

  • Re: XML: Aggiunta di child node con attributi a child node esistente

    LastTeacher dovresti forse ricavarlo con LastChild https://docs.microsoft.com/it-it/dotnet/api/system.xml.xmlnode.lastchild?view=netcore-3.1
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    brobh ha scritto:


    Dato che non funziona, mi chiedevo se a essere sbagliato è qualcosa che riguarda il modo in cui definisco il path del file, se dovrei specificare qual è la root anziché istanziare oggetti XmlNode o se sto proprio sbagliando approccio...
    Nel tuo codice ci sono diversi problemi: innanzitutto, crei gli attributi con i valori dell'oggetto Teacher, ma poi non li vai ad aggiungere all'elemento, quindi non li troverai all'interno del documento XML salvato. Inoltre, stai usando il valore dell'attributo nel costruttore come parametro errato (non si aspetta il valore!).

    Ad esempio, dovresti fare in questo modo:
    
    var attrId = xmlDoc.CreateAttribute("TeacherID");
    attrId.Value = oggettoTeacher.Id.ToString();
    newTeacher.Attributes.Append(attrId);
    
    var attrName = xmlDoc.CreateAttribute("Name");
    attrName.Value = oggettoTeacher.Name;
    newTeacher.Attributes.Append(attrName);
    
    var attrSurname = xmlDoc.CreateAttribute("Surname");
    attrSurname.Value = oggettoTeacher.Surname;
    newTeacher.Attributes.Append(attrSurname);
    
    Inoltre, il percorso XPath che hai usato - "//teacher" - seleziona tutti i nodi di tipo <teacher> ma (se non ricordo male) ti restituisce solo il primo, visto che hai chiamato SelectSingleNode(), quindi andresti a inserire un nuovo elemento dopo il primo e non dopo l'ultimo.

    Non ti serve acquisire un riferimento all'elemento figlio, ti basta avere quello del padre e usare il metodo che consente di accodare un nuovo elemento in fondo ai figli già esistenti:
    
    Teachers.AppendChild(newTeacher);
    
    Il mio invito è quello di scrivere il codice ma consultando prima la documentazione delle classi che stai usando, e/o verificando che i valori specificati vadano a finire nelle proprietà e nei parametri giusti.

    Un ulteriore consiglio è quello di non usare XmlDocument, bensì la classe XDocument nel namespace System.Xml.Linq, che offre un approccio più semplice, più moderno e anche più performante in determinati casi.

    Ad esempio, nel tuo codice stai banalmente navigando nel documento XML tra un elemento e l'altro, ma ricorri a XPath per ogni occasione: questo a mio avviso è sbagliato poiché il parser delle espressioni XPath impiega risorse e tempo per restituirti i risultati ed è fatica sprecata per le necessità di modifica del documento che hai. Usando la classe XDocument, il tuo codice si potrebbe sintetizzare così:
    
    const string nomeFile = @"File.xml";
    
    var xmlDoc = XDocument.Load(nomeFile);
    
    xmlDoc
    	.Element("School")
    	.Element("Teachers")
    	.Add(
    		new XElement("Teacher",
    	  		new XAttribute("TeacherID", oggettoTeacher.Id),
    	  		new XAttribute("Name", oggettoTeacher.Name),
    	  		new XAttribute("Surname", oggettoTeacher.Surname)
    		)
    	);
    
    xmlDoc.Save(nomeFile);
    
    Prendi un po' di tempo per documentarti e studiare gli esempi.

    Ciao!
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    --------------------------------------------------------- [UPDATE] -----------------------------------------------------------------------

    ho applicato il metodo e ha inserito l'elemento, però senza ending tag, in pratica anziché inserirlo così:
    <teacher TeacherID = "1"  Name= "Brian" Surname= " Vieri" > </teacher >
    lo insersce così:
    <teacher TeacherID = "1"  Name= "Brian" Surname= "Vieri" > 
    Può comportare dei problemi nelle future letture del file xml o l'assenza dell' ending tag è indifferente?

    --------------------------------------------------------------------------------------------------------------------------------------------

    Grazie mille della risposta esaustiva Alka!

    Per quanto riguarda il path, peril momento l'ho sostituito con una costante string "filename" dove inserisco il percorso del file e sto passando quella come parametro al posto dell'intero path... Ora mi vedo Linq , grazie intanto dell'ottimo consiglio e del link alla documentazione!

  • Re: XML: Aggiunta di child node con attributi a child node esistente

    brobh ha scritto:


    ho applicato il metodo e ha inserito l'elemento, però senza ending tag [...]
    lo insersce così:
    <teacher TeacherID = "1"  Name= "Brian" Surname= "Vieri" > 
    Può comportare dei problemi nelle future letture del file xml o l'assenza dell' ending tag è indifferente?
    Forse intendi così:
    <teacher TeacherID = "1" Name="Brian" Surname="Vieri" />
    ovvero con la chiusura in fondo />.

    Si tratta di un "self-closing tag". Se non contiene figli, è possibile terminare il nodo con sé stesso in questo modo.

    Se invece è esattamente come l'hai scritto tu, allora sì, prevedo problemi.

    brobh ha scritto:


    Per quanto riguarda il path, peril momento l'ho sostituito con una costante string "filename" dove inserisco il percorso del file e sto passando quella come parametro al posto dell'intero path...
    Non mi riferivo al percorso del file, ma all'XPath (vedi tutorial), ossia quella sintassi che usi per i metodi SelectSingleNode() e SelectNodes() allo scopo di navigare l'albero e ricercare gli elementi che ti interessano.

    Ciao!
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    Grazie per la spiegazione del self-closing tag, ora tutto è più chiaro

    Ora sto cercando di fare il contrario: leggere i valori degli attributi di ognuno degli elementi XML di un nodo e assegnarli ai field di un oggetto che poi inserisco in una lista, mi dà come errore che il riferimento all'oggetto non esiste, ho fatto così:
    
                public static List<Teacher> GetAll()
                {
                    var listOfTeachers = new List<Teacher>();
                   
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.Load(nomeFile);
                       
                            foreach
                                (
                                XmlNode xmlNode in
                                xmlDoc.DocumentElement.ChildNodes[1].ChildNodes
                                )
                            {
                                var myTeacher = new Teacher()
                                {
                                    TeacherId = int.Parse(xmlNode.Attributes["teacherId"].Value),
                                    Name = xmlNode.Attributes["Name"].Value,
                                    Surname = xmlNode.Attributes["Surname"].Value                                
                                };
                                listOfTeachers .Add(myTeacher );
                            }                  
                        return returnedListOfBooks;              
                }
    

    Facendo il debug vedo che è come se il nodo letto fosse vuoto, il DocumentElement risulta "inesistente in questo contesto", idem per ChildNodes e Values, mi chiedo se dipenda dal path inserito qui:
                                xmlDoc.DocumentElement.ChildNodes[1].ChildNodes
    

    Io ho fatto questo ragionamento: parto dal DocumentElement, che è School, vado su ChildNodes[1], che è Teachers, e prendo tutti i suoi ChildNodes... O non è che magari dovrei istanziare XmlNode al posto di richiamare DocumentElement sull'istanza di XmlDocument?
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    brobh ha scritto:


    mi dà come errore che il riferimento all'oggetto non esiste [...]
    Se il documento è uguale a quello che hai postato all'inizio, il riferimento nullo è probabilmente su questa parte di codice:
    
    TeacherId = int.Parse(xmlNode.Attributes["teacherId"].Value),
    
    Il motivo è che il nome dell'attributo "teacherId" lo hai indicato con la lettera minuscola, mentre nel file ha l'iniziale maiuscola, quindi Attributes["teacherId"] restituisce un riferimento nullo, e leggere la proprietà Value di un oggetto null produce ovviamente l'errore in questione.
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    Grazie per le risposte! Ho risolto il problema: non era il nome dell'attributo (nel codice l'avevo scritto bene) ma la presenza di commenti nel file xml (scritti <!-- in questa forma--> ), che non venivano ignorati e quindi sfasavano tutta la struttura!
  • Re: XML: Aggiunta di child node con attributi a child node esistente

    brobh ha scritto:


    Ho risolto il problema: non era il nome dell'attributo (nel codice l'avevo scritto bene)
    La cosa a cui si deve fare più attenzione quando si chiede aiuto su un forum è riportare esattamente il codice come è scritto.

    Se si copia/incolla male o si fanno modifiche manuali, poi chi risponde è costretto a diagnosticare e spiegare errori che alla fine sono inesistenti...
Devi accedere o registrarti per scrivere nel forum
8 risposte