2009-10-01 22 views
6

Nadpisuję metodę, która ma przekazywany XmlReader, potrzebuję znaleźć określony element, dodać atrybut, a następnie albo utworzyć nowy XmlReader lub po prostu wymienić istniejący ze zmodyfikowaną treścią. Używam C# 4.0XmlReader - Potrzebuję edytować element i utworzyć nowy

Badałem za pomocą XElement (Linq), ale nie mogę manipulować istniejącego elementu i dodać atrybut i wartość.

wiem, że XmlWriter ma WriteAttributeString co byłoby fantastyczne, ale znowu nie jestem pewien, jak to wszystko do siebie pasuje

Chciałbym móc zrobić coś takiego --- To jest pseudo-code!

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

Odpowiedz

11

XmlReader/Writer są kolejne strumienie dostępu. Będziesz musiał przeczytać na jednym końcu, przetworzyć strumień, jak chcesz, i napisać go na drugim końcu. Zaletą jest to, że nie trzeba odczytywać całości w pamięci i budować modelu DOM, co można uzyskać za pomocą dowolnego podejścia opartego na XmlDocument.

Ta metoda powinna Ci zacząć:

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

To nie jest tak czysty, jak pochodzący własną XmlWriter, ale uważam, że jest to o wiele łatwiejsze.

[EDIT]

Przykładem, jak można otworzyć dwa strumienie na raz może być coś takiego:

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

nie można łatwo zrobić z XmlReader - przynajmniej nie bez czytania całego dokumentu XML z czytnika, futzing z nim, a następnie utworzenie nowego XmlReader od wyniku. To jednak pokonuje wiele kwestii związanych z używaniem XmlReader - czyli możliwości przesyłania strumieniowego dużych dokumentów.

Mogłabyś potencjalnie pochodzą z XmlReader, spedycja najbardziej metoda połączenia z istniejącym czytnika ale przechwytując je w razie potrzeby dodać kolejne atrybuty itp ... ale podejrzewam, że kod będzie naprawdę dość skomplikowane i delikatne.

+0

Dzięki za odpowiedź, pomyślałem (mam nadzieję), że byłaby to miła prosta operacja. Nie mogę być jedyną osobą, która kiedykolwiek chciała czytać w XML do pewnego elementu, edytować go, a następnie wypisać zmieniony wynik. - Jeśli słyszysz więcej przekleństw niż zwykle w Reading dzisiaj .... to ja !! –

+0

@ Hill: Łatwo to zrobić, po prostu nie w streamingu Obawiam się :( –

+0

Subclassing 'XmlReader' (właściwie ty podklasę' XmlTextReader', chyba że masz ochotę wdrożyć wiele abstrakcyjnych metod) jest interesujące wyzwanie: –

1

Naprawiłem go stosując następujący Duct Tape kodowania

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

czuję się brudny, by to zrobić w ten sposób, ale to działa ....

+1

Nie byłbym tak pewien, że to działa. Czytelnik, którego zwrócisz, nie będzie być w stanie przejść do elementu po tym, ponieważ ten element (w rzeczywistości cała reszta dokumentu XML, który przetwarzał oryginalny czytnik) nie znajduje się w jego strumieniu. –

0

Wolałbym wolą załadować XML do XmlDocument obiekt i użyj kolekcji Attributes, aby zmodyfikować wartość i wywołać metodę Save, aby zaktualizować tę wartość. Poniższy kod działa dla mnie.

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

}

mam obserwowano również podczas korzystania XmlWriter lub XMLSerializer te odstępy nie zostały prawidłowo zachowane, to może być irytujące czasami

-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 mogą być zapisywane z powrotem do pliku. ..to jest edytowane xml

Powiązane problemy