tags:

views:

447

answers:

6

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 : http://stackoverflow.com/questions/1457033/c-insert-a-strongly-typed-object-as-node-in-existing-xml-document

+1  A: 

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");
Simon Fox
Oops, forgot to mention I'm stuck on a .net v2 project.
JL
+3  A: 

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 ?

Frederik Gheysels
Thats fine, what I really need is a hook on how to get the strongly typed document into an already existing document structure.
JL
What exactly do you mean ? I think I do not understand what you're trying to say ...
Frederik Gheysels
Ok - I'll update the question...
JL
@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
Why do you think you have a lot of chance of file corruption ?
Frederik Gheysels
From existing test results on code I already have using serialization as a whole.
JL
+1  A: 

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.

Oplopanax
this works in .net 2.0
Oplopanax
I can see from your edit that this answer does not answer your question! Sorry.
Oplopanax
Yeah sorry I wasn't more clear first time round. Hopefully now it makes more sense.
JL
+1  A: 

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.
eglasius
A: 

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.

mhenrixon
+1  A: 

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);
Pavel Minaev
ok let me try this.
JL
Where does ser come from?
JL
Check the first line of code. You might want to make it a `private static` field in your class so that you don't recreate the serializer every time (it is reusable).
Pavel Minaev
Thank you, will run a quick test.
JL
Getting this error message : "WriteStartDocument cannot be called on writers created with ConformanceLevel.Fragment."
JL
Ah, too bad, I hoped to avoid the extra intermediary document, but it seems there's no way around this. Very well, try the updated version.
Pavel Minaev