XmlSerializer,它序列化所有和仅嵌套的值类型属性

时间:2018-04-09 22:05:37

标签: c# reflection xml-serialization

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

我发现用英语来概括它有点难(不是母语,所以欢迎编辑措辞),所以我会解释:

  • 如果属性是值类型 - 序列化其名称和值
  • 如果属性是Nullable类型:如果其值为非null,请执行上述操作(实际上,序列化Nullable' s Value属性);否则,不要将其序列化。

  • 如果属性属于类类型,则序列化类'根据上述属性,不会序列化类名

例如,这个:

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>

现在,最后一个子弹要求邀请递归,但据我所知answer,它是父类&#39;可以省略WriteXml标记的<UndesiredTag>,而嵌套类可以省略。

那么,我现在拥有的东西(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();
                    }   
                }
            }       
        }    
    }
}

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

1 个答案:

答案 0 :(得分:1)

由于你无论如何都覆盖了所有的默认序列化,对我来说放弃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);
                }
            }
        }
    }
}