2009-10-01 2 views
6

Sto sovrascrivendo un metodo in cui è passato un XmlReader, devo trovare un elemento specifico, aggiungere un attributo e quindi creare un nuovo XmlReader o semplicemente sostituire quello esistente con il contenuto modificato. Sto usando C# 4.0XmlReader - Devo modificare un elemento e produrne uno nuovo

Ho studiato usando XElement (Linq) ma non riesco a manipolare un elemento esistente e aggiungere un attributo e un valore.

So che il XmlWriter ha WriteAttributeString che sarebbe fantastico, ma ancora una volta non sono sicuro di come si integra il tutto

mi piacerebbe essere in grado di fare qualcosa di simile --- Questa è pseudo-codice!

public XmlReader DoSomethingWonderful(XmlReader reader) 
{ 
    Element element = reader.GetElement("Test"); 
    element.SetAttribute("TestAttribute","This is a test"); 
    reader.UpdateElement(element); 
    return reader; 
} 

risposta

11

XmlReader/Writer sono flussi accesso sequenziale. Dovrai leggere da una parte, elaborare lo stream come vuoi e scriverlo dall'altra parte. Il vantaggio è che non è necessario leggere l'intera cosa in memoria e creare un DOM, che è ciò che otterresti con qualsiasi approccio basato su XmlDocument.

Questo metodo dovrebbe iniziare:

private static void PostProcess(Stream inStream, Stream outStream) 
{ 
    var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; 

    using (var reader = XmlReader.Create(inStream)) 
    using (var writer = XmlWriter.Create(outStream, settings)) { 
     while (reader.Read()) { 
      switch (reader.NodeType) { 
       case XmlNodeType.Element: 
        writer.WriteStartElement(reader.Prefix, reader.Name, reader.NamespaceURI); 
        writer.WriteAttributes(reader, true); 

        // 
        // check if this is the node you want, inject attributes here. 
        // 

        if (reader.IsEmptyElement) { 
         writer.WriteEndElement(); 
        } 
        break; 

       case XmlNodeType.Text: 
        writer.WriteString(reader.Value); 
        break; 

       case XmlNodeType.EndElement: 
        writer.WriteFullEndElement(); 
        break; 

       case XmlNodeType.XmlDeclaration: 
       case XmlNodeType.ProcessingInstruction: 
        writer.WriteProcessingInstruction(reader.Name, reader.Value); 
        break; 

       case XmlNodeType.SignificantWhitespace: 
        writer.WriteWhitespace(reader.Value); 
        break; 
      } 
     } 
    } 
} 

Questo non è così pulito come derivanti proprio XmlWriter, ma trovo che è molto più facile.

[EDIT]

Un esempio di come si potrebbe aprire due flussi in una volta potrebbe essere qualcosa di simile:

using (FileStream readStream = new FileStream(@"c:\myFile.xml", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write)) { 
    using (FileStream writeStream = new FileStream(@"c:\myFile.xml", FileMode.OpenOrCreate, FileAccess.Write)) { 
    PostProcess(readStream, writeStream); 
    } 
} 
1

Non si può facilmente fare questo con XmlReader - almeno, non senza leggere l'intero documento XML in dal lettore, futzing con esso e quindi la creazione di un nuovo XmlReader dal risultato. Ciò sconfigge molto il problema dell'uso di XmlReader, vale a dire la possibilità di eseguire lo streaming di documenti di grandi dimensioni.

Si potrebbe potenzialmente derivano da XmlReader, spedizioni più chiamate di metodo per il lettore esistente, ma loro intercettazione eventualmente aggiungere attributi extra ecc ... ma ho il sospetto che il codice sarebbe stato davvero molto complesso e fragile.

+0

Grazie per la risposta, ho pensato (sperato) che sarebbe stata una semplice operazione. Non posso essere l'unica persona che abbia mai voluto leggere in XML fino a un certo elemento, modificarlo e quindi produrre il risultato modificato. - Se senti più imprecazioni del solito a Reading oggi .... sono io !! –

+0

@Phill: È facile farlo, ma non in modo streaming temo :( –

+0

Sottoclasse 'XmlReader' (in realtà sottoclassi' XmlTextReader', a meno che tu non abbia voglia di implementare un sacco di metodi astratti) è un interessante sfida –

1

ho riparato utilizzando la seguente nastro adesivo codifica

public XmlReader FixUpReader(XmlReader reader) 
    { 
     reader.MoveToContent(); 

     string xml = reader.ReadOuterXml(); 

     string dslVersion = GetDSLVersion(); 
     string Id = GetID(); 

     string processedValue = string.Format("<ExampleElement dslVersion=\"{1}\" Id=\"{2}\" ", dslVersion, Id); 
     xml = xml.Replace("<ExampleElement ", processedValue); 
     MemoryStream ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(xml)); 
     XmlReaderSettings settings = new XmlReaderSettings(); 

     XmlReader myReader = XmlReader.Create(ms); 
     myReader.MoveToContent(); 
     return myReader; 
    } 

mi sento sporco per farlo in questo modo, ma si sta lavorando ....

+1

Non sarei così sicuro che funzioni. Il lettore che ritorni non ha intenzione di essere in grado di spostarsi sull'elemento dopo questo, perché quell'elemento (in realtà, tutto il resto del documento XML che il lettore originale stava elaborando) non è nel suo backing stream. –

0

vorrei piuttosto preferisco per caricare il codice XML in XmlDocument oggetto e utilizzare la raccolta Attributes per modificare il valore e chiamare il metodo Save per aggiornare questo valore. Sotto il codice funziona per me.

public static void WriteElementValue (string sName, string element, string value) 
{ 
    try 
    { 
    var node = String.Format("//elements/element[@name='{0}']", sName); 
    var doc = new XmlDocument { PreserveWhitespace = true }; 
    doc.Load(configurationFileName); 

    var nodeObject = doc.SelectSingleNode(node); 

    if (nodeObject == null) 
     throw new XmlException(String.Format("{0} path does not found any matching 
     node", node)); 

    var elementObject = nodeObject[element]; 

    if (elementObject != null) 
    { 
     elementObject.Attributes["value"].Value = value; 
    } 

    doc.Save(configurationFileName); 
} 
catch (Exception ex) 
{ 
    throw new ExitLevelException(ex, false); 
} 

}

ho anche osservato quando si utilizza XmlWriter o XmlSerializer gli spazi bianchi non sono stati conservati in modo corretto, questo potrebbe essere fastidioso a volte

-2
 string newvalue = "10"; 
     string presentvalue = ""; 
     string newstr = ""; 
     XmlReader xmlr = XmlReader.Create(new StringReader(str)); 

     while (xmlr.Read()) 
     { 
      if (xmlr.NodeType == XmlNodeType.Element) 
      { 
       if (xmlr.Name == "priority") 
       { 
        presentvalue = xmlr.ReadElementContentAsString(); 
        newstr = str.Replace(presentvalue, newvalue); 
       } 
      } 

     } 

// newstr può essere scritto di nuovo al file. ..questo è il xml modificato