简体   繁体   中英

C#, insert strongly typed object into XML Document as Element

I have an XML document that contains the following structure:

Its more or less a collection of Events:

<Events>
  <Event>
    <DateTime></DateTime>
    <EventType></EventType>
    <Result></Result>
    <Provider></Provider>
    <ErrorMessage></ErrorMessage>
    <InnerException></InnerException>
  </Event>
</Events>

In C# I have a persistent object called Event:

Now given that the document already exists, and saved to file... I call :

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(dataPath);

Now how can I add a new Event item to events?

I've got a strongly typed Event item in my C# code, and want it inserted into the Events collection in the XML object as last child.

I guess this is what I am really asking : https://stackoverflow.com/questions/1457033/c-insert-a-strongly-typed-object-as-node-in-existing-xml-document

Take a look at the Xml Serialization attributes.

You can do this:

[XmlRoot("Event")]
public class Event
{

    [XmlElement("DateTime")]
    public string DateTime 
    {
        get;
        set;
    }

    [XmlElement("EventType")]
    public EnumReportingEventType EventType
    {
        get;
        set;
    }

    [XmlElement("Result")]
    public EnumReportingResult Result
    {
        get;
        set;
    }

    [XmlElement("Provider")]
    public string Provider
    {
        get;
        set;
    }

    [XmlElement("ErrorMessage")]
    public string ErrorMessage
    {
        get;
        set;
    }

    [XmlElement("InnerException")]
    public string InnerException
    {
        get;
        set;
    }
}

In fact, if the properties of your class have the same name as the elements in your Xml, you do not have to use the XmlElement attributes.

Then, you can use the XmlSerializer to serialize and deserialize.

Edit: Then, wouldn't it be better to create a type which represent the entire type that is stored in the existing xml ?
Deserialize it, give a value to the additional property, and serialize it back ?

If you are using .Net 3.5 you can use Linq to XML, something like the following will work

XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Event document"),
new XElement("Events", 
    new XElement ("Event",
        new XElement("DateTime", event.DateTime),
        new XElement("EventType", event.EventType),
        new XElement("Result", event.Result),
        new XElement("Provider", event.Provider),
        new XElement("ErrorMessage", event.ErrorMessage),
        new XElement("InnerException", event.InnerException)
    )
 ));


doc.Save(@"c:\sample.xml");

If you have an existing xml document that you want to append to somthing like the following is required.

XDocument doc = XDocument.Load(@"c:\sample.xml");
XElement events = doc.Element(XName.Get("Events"));
events.Add(new XElement ("Event",
        new XElement("DateTime", event.DateTime),
        new XElement("EventType", event.EventType),
        new XElement("Result", event.Result),
        new XElement("Provider", event.Provider),
        new XElement("ErrorMessage", event.ErrorMessage),
        new XElement("InnerException", event.InnerException)
));

doc.Save(@"c:\sample.xml");

Insert a XmlElement field near the end of the "Event" class like so:

[System.Xml.Serialization.XmlAnyElementAttribute()]
public System.Xml.XmlElement Any { get; set }

You can name it whatever you want as long as you have the "XmlAnyElementAttribute" on it.

You can use something like the following to serialize a strongly-typed object into this field:

MyParentObject parent = new MyParentObject(){ ... };

MyObject obj = new MyObject(){ /*... initialize*/ };
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
XmlDocument doc = new XmlDocument();

using (StringWriter sw = new StringWriter())
{
    ser.Serialize(sw, obj);
    doc.LoadXml(sw.ToString());
}

parent.Any = (XmlElement)doc.DocumentElement;

The serialized XML will append nicely to your class, it will event include the correct namespaces.

Just hand code it using the XmlDocument. You can add a class that does the conversion or insertion into the document you are going to save.

This is based on the restriction of .net 2.0 and what you said in these comments:

  • @Fred, I want to try minimize the write time, hence the reason for not writing all at once, the less write time in this app, the less chance of file corruption. – JL 16 mins ago
  • Why do you think you have a lot of chance of file corruption ? – Frederik Gheysels 9 mins ago
  • From existing test results on code I already have using serialization as a whole.

Assuming that your Event class can already be serialized the way you want using XmlSerializer , you can do the following:

XmlSerializer ser = new XmlSerializer(typeof(Event));

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(dataPath);

Event evt = ...;

XmlDocument evtDoc = new XmlDocument();
using (XmlWriter writer = evtDoc.CreateNavigator().AppendChild())
{
     ser.Serialize(writer, evt);
}
XmlNode evtNode = evtDoc.RemoveChild(evtDoc.DocumentElement);

XmlNode events = xmlDoc.SelectSingleNode("/Events");
events.AppendChild(evtNode);

What you are looking to do is something like:

doc.ChildNode[0].AppendChild(MethodToReturnADeserializedObject(event));

Create a method to deserialize the event object into an xml node. Then use AppendChild to insert that as the last element amongst it's child nodes.

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