简体   繁体   中英

how to read & write xml file in C# not rely on the tag name?

Thank you very much for reading my question.

the bottom is the sample of my xml file.please refer that.

i did some xml files before, but by "CMarkXml". "IntoElement, OutofElement", is very clear.

but when C#...i was lost..

1: how to read & write my xml file without using the tag name. i see some articles about operation on xml file by c#, but all assumed that known the tag name.

2: if without tag name, it is very difficult or not recommend. then how to read & write my xml file by XmlDocument? (sorry, but no Ling please, i am very faint with that...).

3: my idear is, for the xml file, get out some section, we still could parse the section by xmldocument.

4: for the write/modify the xml file, of course, should contain delete some section, delete some "leaf", change the attributes...

Thank you very much for reading the long question, and any help i will very appreciate. If you have a good sample code but not continent paste them here, could you send it to "erlvde@gmail.com"?

<root>
    <a>i belong to a</a>
    <b>
        <bb>
            <bb>1</bb>
            <bb>2</bb>
            <bb>3</bb>
            <bb>4</bb>
            <bb>5</bb>
        </bb>
        <bb>
            <bb>1</bb>
            <bb>2</bb>
            <bb>3</bb>
            <bb>4</bb>
            <bb>5</bb>
        <bb>
    ....(other <bb>)
    </b>
</root>

Read your xml into XmlDocument :

var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("XML HERE");

Access child nodes:

xmlDocument.ChildNodes[1]

But it's also true that it's very error prone

You can also check if you have child nodes at all:

xmlDocument.HasChildNodes

And get number of child nodes:

xmlDocument.ChildNodes.Count

It looks to me like your elements names contain identifiers. If that is the case, and you have control over the XML schema, I would highly recommend changing your XML to contain elements and/or attributes indicating your identifiers and then use the built in XmlSerializer class for serializing to and from XML. It has many modifiers available, such as XmlElement and XmlAttribute among many others, for formatting the output.

Here is a tutorial to get you started.

If possible, change your XML to something like following which would make it far simpler to manipulate...again if changing the schema is a possibility.

<root>
    <a>i belong to a</a>
    <b>
        <bb id="1">
            <bb>1</bb>
            <bb>2</bb>
            <bb>3</bb>
            <bb>4</bb>
            <bb>5</bb>
        </bb>
        <bb id="2">
            <bb>1</bb>
            <bb>2</bb>
            <bb>3</bb>
            <bb>4</bb>
            <bb>5</bb>
        <bb>
    </b>
</root>

Edit this edit reflects the changes you made to your XML


Here is a simple console application which will serialize an object to an XML file and then rehydrate it.

Expected XML

<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <a>i belong to a</a>
  <b>
    <bb>
      <bb>1</bb>
      <bb>2</bb>
      <bb>3</bb>
      <bb>4</bb>
      <bb>5</bb>
    </bb>
    <bb>
      <bb>1</bb>
      <bb>2</bb>
      <bb>3</bb>
      <bb>4</bb>
      <bb>5</bb>
    </bb>
  </b>
</root>

Simple Console Application Demonstration

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var items = new root
            {
                a = "i belong to a",
                b = new List<bb>
                {
                    new bb
                    {
                        bbClassProperty = new List<int>
                        {
                            1,
                            2,
                            3,
                            4,
                            5
                        }
                    },
                    new bb
                    {
                        bbClassProperty= new List<int>
                        {
                            1,
                            2,
                            3,
                            4,
                            5
                        }
                    }
                }
            };

            XmlSerializer serializer = new XmlSerializer(typeof(root));

            using (var textWriter = new StreamWriter(@"C:\root.xml"))
            {
                serializer.Serialize(textWriter, items);
                textWriter.Close();
            }

            using (var stream = new StreamReader(@"C:\root.xml"))
            {
                var yourObject = serializer.Deserialize(stream);
            }

            Console.Read();
        }
    }

    #region [Classes]

    public class root
    {
        public string a { get; set; }

        public List<bb> b { get; set; }
    }

    public class bb
    {
        [XmlElement("bb")]
        public List<int> bbClassProperty { get; set; }
    }

    #endregion
}

Look into the ChildNodes (and similar) properties and methods on your XmlElement object. These will let you iterate over the children of a node and you can then ask that node for its name.

If you have a XmlNode object, you can use XMLNode.FirstChild to get the child, if it has any. You can also use XMLNode.NextSibling to get the next Node of the same parent node. Why can't you use the names of the nodes? It's the easiest and most common way. Especially if you use XPath or similar.

XPath is also the answer to your second question.

U can use the class XML reader, a simple example is given here .

using System;
using System.Xml;

class Program
{
    static void Main()
    {
    // Create an XML reader for this file.
    using (XmlReader reader = XmlReader.Create("perls.xml"))
    {
        while (reader.Read())
        {
        // Only detect start elements.
        if (reader.IsStartElement())
        {
            // Get element name and switch on it.
            switch (reader.Name)
            {
            case "perls":
                // Detect this element.
                Console.WriteLine("Start <perls> element.");
                break;
            case "article":
                // Detect this article element.
                Console.WriteLine("Start <article> element.");
                // Search for the attribute name on this current node.
                string attribute = reader["name"];
                if (attribute != null)
                {
                Console.WriteLine("  Has attribute name: " + attribute);
                }
                // Next read will contain text.
                if (reader.Read())
                {
                Console.WriteLine("  Text node: " + reader.Value.Trim());
                }
                break;
            }
        }
        }
    }
    }
}

The input file text is:

<?xml version="1.0" encoding="utf-8" ?>
<perls>
    <article name="backgroundworker">
    Example text.
    </article>
    <article name="threadpool">
    More text.
    </article>
    <article></article>
    <article>Final text.</article>
</perls>

Output

Start element.

Start element.

Has attribute name: backgroundworker

Text node: Example text.

Start element.

Has attribute name: threadpool

Text node: More text.

Start element.

Text node:

Start element.

Text node: Final text. enter code here

You can use the following code to if the file does not contain the headers, in the example above.

XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Fragment;
reader = XmlReader.Create(filePath, settings)

Would something like this help?

void Iterate(XmlNode parent) {
    //do something with
    //parent.Name
    //parent.Value
    //parent.Attributes

    foreach(XmlNode child in parent.ChildNodes) {
        Iterate(child);
    }
}

XmlDocument document = new XmlDocument();
document.Load(filename);

XmlNode parent = document.DocumentElement;
Iterate(parent);

You could also store it like that (sorry for any syntactical error, didn't run it)

public class Document {
    public Element DocumentElement { set; get; }

    private void Load(string fileName) {
        XmlDocument document = new XmlDocument();
        document.Load(fileName);

        DocumentElement = new Element(this, null);
        DocumentElement.Load(document.DocumentElement);
    }
}

public class Element {
    public string Name { set; get; }
    public string Value { set; get; }
    //other attributes

    private Document document = null;
    private Element parent = null;
    public Element Parent { get { return parent; } }
    public List<Element> Children { set; get; }
    private int order = 0;

    public Element(Document document, Element parent) {
        Name = "";
        Value = "";
        Children = new List<LayoutElement>();

        this.document = document;
        this.parent = parent;
        order = parent != null ? parent.Children.Count + 1 : 1;
    }

    private Element GetSibling(bool left) {
        if(parent == null) return null;
        int add = left ? -1 : +1;
        Element sibling = parent.Children.Find(child => child.order == order + add);
        return sibling;
    }

    public Element GetLeftSibling() {
        return GetSibling(true);
    }

    public Element GetRightSibling() {
        return GetSibling(false);
    }

    public void Load(XmlNode node) {
        Name = node.Name;
        Value = node.Value;
        //other attributes

        foreach(XmlNode nodeChild in node.Children) {
            Element child = new Element(document, this);
            child.Load(nodeChild);
            Children.Add(child);
        }
    }
}

Document document = new Document();
document.Load(fileName);

For changing/deleting right now you could iterate the tree and find elements by name, but since name is not unique, you would affect many elements at once. You could add an unique id in every tag like

<bb id="bb1"/>

Then read it in Load function like

id = ((XmlElement)node).GetAttribute("id");

and use this id to iterate through the tree. Sorry I don't have time right now to provide something more detailed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM