XmlReader - I need to edit an element and produce a new one

前端 未结 5 2109
花落未央
花落未央 2020-12-20 15:54

I am overriding a method which has an XmlReader being passed in, I need to find a specific element, add an attribute and then either create a new XmlReader or just replace t

相关标签:
5条回答
  • 2020-12-20 16:28

    I fixed it using the following duct tape coding

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

    I feel dirty for doing it this way but it is working....

    0 讨论(0)
  • 2020-12-20 16:32

    XmlReader/Writer are sequential access streams. You will have to read in on one end, process the stream how you want, and write it out the other end. The advantage is that you don't need to read the whole thing into memory and build a DOM, which is what you'd get with any XmlDocument-based approach.

    This method should get you started:

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

    This is not quite as clean as deriving your own XmlWriter, but I find that it's much easier.

    [EDIT]

    An example of how you would open two streams at once might be something like this:

    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);
      }
    }
    
    0 讨论(0)
  • 2020-12-20 16:37

    I would rather prefer to load the xml into XmlDocument object and use Attributes collection to modify the value and call Save method to update this value. Below code works for 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);
    }
    

    }

    I've also observed when you use XmlWriter or XmlSerializer the whitespaces were not correctly preserved, this could be annoying sometimes

    0 讨论(0)
  • 2020-12-20 16:45
            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 can be written back to file... that is the edited xml

    0 讨论(0)
  • 2020-12-20 16:46

    You can't easily do this with XmlReader - at least, not without reading the whole XML document in from the reader, futzing with it and then creating a new XmlReader from the result. That defeats a lot of the point of using XmlReader though - namely the ability to stream large documents.

    You could potentially derive from XmlReader, forwarding most method calls to the existing reader but intercepting them where appropriate to add extra attributes etc... but I suspect that code would be really quite complex and fragile.

    0 讨论(0)
提交回复
热议问题