简体   繁体   中英

Serialize object to XML WITHIN a parent element

I've got a WPF C# program and at one point I need to serialize objects to XML. In other places, I've been using this:

TextWriter writer = new StreamWriter(xmlFilePath);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(MYOBJECT_TYPE));

try
{
    xmlSerializer.Serialize(writer, MYOBJECT);

}
catch (Exception ex)
{
    MessageBox.Show("Exception occured while writing to Xml" + ex.Message);
}
finally
{
    writer.Close();
}

This is fantastic, but this means I have to have a different XML file for every object I want to serialize. How do I use this method (with the least amount of modifications) to serialize the object to the XML WITHIN a parent element ? That way, when I want to deserialize the object later, I can just find the element that I want, and deserialize everything within that element.

As requested, here is CreateDefaultXml(); :

static void CreateDefaultXml()
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml("<StoredObjects></StoredObjects>");
    XmlNode root = doc.DocumentElement;
    try
    {
        doc.Save(xmlFilePath);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception occured while creating Xml" + ex.InnerException);
    }
}

EDIT:

Currently, this is what I've got (but it throws an exception There was an error generating the XML document. )

if (!File.Exists(xmlFilePath))
    CreateDefaultXml();

XDocument doc = XDocument.Load(xmlFilePath);
var element = doc.Descendants("Object").Where(x => x.Attribute("Name").Value.Equals("objectName")).SingleOrDefault();

if (element == null)
{
    element = new XElement("Object", new XAttribute("Name", objectName));
    doc.Element("StoredObjects").Add(element);
}

XmlWriter writer = element.CreateWriter();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(MYOBJECT_TYPE));

try
{
    xmlSerializer.Serialize(writer, MYOBJECT);

}
catch (Exception ex)
{
    MessageBox.Show("Exception occured while writing to Xml: " + ex.Message);
}
finally
{
    writer.Close();
    doc.Save(xmlFilePath);
}

You are trying to serialize directly to some nested XElement inside an XDocument using XmlSerializer . Unfortunately, it seems that, when serializing directly to a LINQ-to-XML XElement using XmlSerializer together with XContainer.CreateWriter() , it is actually necessary to serialize to an empty XDocument thereby creating its root element. (I am not sure why this restriction exists, but it does.) Since this doesn't meet your needs, it is easy to serialize to a temporary XDocument , remove its root node, then add that node to your overall element hierarchy.

Also, when adding an object to your database using an objectName that already has XML data stored, you need to remove the old data.

I refactored your code into extension methods to accomplish this:

public static class XmlExtensions
{
    public static T Deserialize<T>(this XContainer element, XmlSerializer serializer = null)
    {
        using (var reader = element.CreateReader())
        {
            object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
            if (result is T)
                return (T)result;
        }
        return default(T);
    }

    public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer = null)
    {
        var doc = new XDocument();
        using (var writer = doc.CreateWriter())
            (serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj);
        var element = doc.Root;
        if (element != null)
            element.Remove();
        return element;
    }

    public static XName ContainerElementName { get { return (XName)"Object"; } }

    public static XName ContainerAttributeName { get { return (XName)"Name"; } }

    public static XElement SetItem<T>(this XDocument doc, string attributeValue, T obj)
    {
        return doc.SetItem(ContainerElementName, ContainerAttributeName, attributeValue, obj);
    }

    static XElement SetItem<T>(this XDocument doc, XName containerElementName, XName containerAttributeName, string attributeValue, T obj)
    {
        if (doc == null || containerElementName == null || containerAttributeName == null || attributeValue == null)
            throw new ArgumentNullException();
        if (doc.Root == null)
            throw new ArgumentException("doc.Root == null");
        var container = doc.Root.Elements(containerElementName).Where(e => (string)e.Attribute(containerAttributeName) == attributeValue).SingleOrDefault();
        if (container == null)
        {
            container = new XElement(containerElementName, new XAttribute(containerAttributeName, attributeValue));
            doc.Root.Add(container);
        }
        else
        {
            // Remove old content.
            container.RemoveNodes();
        }

        var element = obj.SerializeToXElement();
        container.Add(element);
        return element;
    }

    public static T GetItem<T>(this XDocument doc, string attributeValue)
    {
        return doc.GetItem<T>(ContainerElementName, ContainerAttributeName, attributeValue);
    }

    static T GetItem<T>(this XDocument doc, XName containerElementName, XName containerAttributeName, string attributeValue)
    {
        if (doc == null || containerElementName == null || containerAttributeName == null || attributeValue == null)
            throw new ArgumentNullException();
        if (doc.Root == null)
            throw new ArgumentException("doc.Root == null");
        var container = doc.Root.Elements(containerElementName).Where(e => (string)e.Attribute(containerAttributeName) == attributeValue).SingleOrDefault();
        if (container == null)
            return default(T);
        var element = container.Elements().SingleOrDefault();
        if (element == null)
            return default(T);
        return element.Deserialize<T>();
    }

    public static XDocument CreateDefaultXDocument()
    {
        var xml = @"<StoredObjects></StoredObjects>";
        return XDocument.Parse(xml);
    }
}

Now you can do

doc.AddItem(MYOBJECT, objectName);

And later

var MYOBJECT2 = doc.GetItem<MYOBJECT_TYPE>(objectName);

Example fiddle .

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