序列化所有且仅嵌套值类型属性的 XmlSerializer

XmlSerializer that serializes all and only nested value types properties

我需要序列化一个 class 以便序列化将包含所有嵌套的 值类型 属性。

我发现用英语概括它有点困难(不是母语人士,所以欢迎修改措辞),所以我会解释一下:

例如,这个:

public class SerializeMe
{
    public int A { get; set; }
    public int? B { get; set; }
    public int? C { get; set; }
    public MyClass MyClass { get; set;}
}

public class MyClass
{
    public int Z { get; set;}
}

如果像这样实例化:

public static void Main()
{
    var instance = new SerializeMe
    {
        A = 1,
        B = 3,
        MyClass = new MyClass { Z = 2},
    });
}

应该这样序列化:

<SerializeMe>
  <A>1</A>
  <B>3</B>
  <Z>2</Z>
</SerializeMe>

但是我不知道最后一个子弹怎么做,我以:

结尾
<SerializeMe>
  <A>1</A>
  <B>3</B>
  <UndesiredTag><Z>2</Z></UndesiredTag>
</SerializeMe>

现在,最后一个项目符号要求需要递归,但据我所知 ,父 class' WriteXml 可以省略 <UndesiredTag> 标签,而嵌套的 class 不能。

那么,我目前拥有的 (fiddle):

using System;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.IO;

public class SerializeMe : IXmlSerializable
{
    public int A { get; set; }
    public int? B { get; set; }
    public int? C { get; set; }
    public MyClass MyClass { get; set;}

    public void WriteXml(XmlWriter writer)
    {
        Program.WriteXml<SerializeMe>(writer, this);      
    }
    public void ReadXml(XmlReader reader) {}    
    public XmlSchema GetSchema() { return null; }
}

[AttributeUsage(AttributeTargets.Class)]
public class Nested : Attribute
{}

[Nested]
public class MyClass : IXmlSerializable
{
    public int Z { get; set;}

    public void WriteXml(XmlWriter writer)
    {
        Program.WriteXml<MyClass>(writer, this);
    }
    public void ReadXml(XmlReader reader) {}    
    public XmlSchema GetSchema() { return null; }
}

public class Program
{
    public static void Main()
    {
        var s = XmlSerialize<SerializeMe>(new SerializeMe
        {
            A = 1,
            B = 3,
            MyClass = new MyClass { Z = 2},
        });
        Console.WriteLine(s);
    }
    public static string XmlSerialize<T>(T entity) where T : class
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.OmitXmlDeclaration = true;

        XmlSerializer xsSubmit = new XmlSerializer(typeof(T));
        StringWriter sw = new StringWriter();
        using (XmlWriter writer = XmlWriter.Create(sw, settings))
        {
            var xmlns = new XmlSerializerNamespaces();
            xmlns.Add(string.Empty, string.Empty);

            xsSubmit.Serialize(writer, entity, xmlns);
            return sw.ToString();
        }
    }

    public static void WriteXml<T>(XmlWriter writer, T obj)
    {
        PropertyInfo[] props = obj.GetType().GetProperties();
        foreach (var prop in props)
        {
            var val = prop.GetValue(obj);
            if (val != null)
            {
                if (prop.PropertyType.IsValueType || 
                    prop.PropertyType.IsGenericType && 
                    prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    writer.WriteElementString(prop.Name, val.ToString());
                }
                else
                {
                    if (prop.PropertyType.GetCustomAttribute(typeof(Nested)) != null)
                    {
                        writer.WriteStartElement("UndesiredTag"); // If only I could use an empty string...
                        ((dynamic)val).WriteXml(writer);
                        writer.WriteEndElement();
                    }   
                }
            }       
        }    
    }
}

请注意,我当前的代码假定只有一层嵌套。如果你认为你可以使用递归来解决我的问题,那会更好——因为你允许多个嵌套级别。

既然你覆盖了所有的默认序列化,对我来说似乎更简单的是放弃 XmlSerializer 并完全自己做。

public static void Main()
{
    var s = XmlSerialize(new SerializeMe
    {
        A = 1,
        B = 3,
        MyClass = new MyClass { Z = 2 },
    });
    Console.WriteLine(s);
}

public static string XmlSerialize(object entity)
{
    var buf = new StringBuilder();
    using (var writer = XmlWriter.Create(buf, new XmlWriterSettings() {
        OmitXmlDeclaration = true,
        Indent = true
    }))
    {       
        WriteElement(writer, entity);
    }

    return buf.ToString();
}

static void WriteElement(XmlWriter writer, object obj)
{
    writer.WriteStartElement(obj.GetType().Name);       
    WriteElementProperties(writer, obj);
    writer.WriteEndElement();
}

static void WriteElementProperties(XmlWriter writer, object obj)
{
    foreach (var prop in obj.GetType().GetProperties())
    {
        var val = prop.GetValue(obj);
        if (val != null)
        {
            if (prop.PropertyType.IsValueType ||
                prop.PropertyType.IsGenericType &&
                prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                writer.WriteElementString(prop.Name, val.ToString());
            }
            else
            {
                if (prop.PropertyType.GetCustomAttribute(typeof(Nested)) != null)
                {
                    WriteElementProperties(writer, val);
                } else {
                    WriteElement(writer, val);
                }
            }
        }
    }
}