IXmlSerializable的通用实现

时间:2012-05-15 22:33:52

标签: c# xml generics serialization

我有一个用户定义的类,我的代码中的几乎所有对象都会扩展。我想在这个类上实现iXmlSerializeable,这样我就可以在基类级别控制对象的序列化方式。具体来说,我需要以特定方式处理对象中的DateTime字符串。是的,可能有更好的方法来欺骗应用程序从序列化的DateTime中剥离TimeZone信息。然而,我的要求迫使我忽视这张脸。我需要一种方法将其写入基本上单个函数,该函数不必在应用程序中的任何其他位置影响或更改代码,以实现此效果。

这是迄今为止的实现(仅限读者,甚至尚未启动作者):

    public void ReadXml(System.Xml.XmlReader reader)
    {
        //loop through properties, Read property by name, converting as necessary based on property type.
        Type curType = this.GetType();

        PropertyInfo[] pis;
        PropertyInfo prop = null;
        List<PropertyInfo> piList = new List<PropertyInfo>();

        //get all fields and properties (this does not need to be recursive, properties of the underlying object DO appear at this stage, unlike fields.
        pis = curType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        foreach (PropertyInfo pi in pis)
            piList.Add(pi);

        bool isEmpty = reader.IsEmptyElement;

        reader.ReadStartElement();

        if (!isEmpty)
        {
            bool isProp = false;

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                if (!reader.IsEmptyElement)
                {
                    if (piList.Select(a => a.Name).Contains(reader.Name))
                        isProp = true;

                    if (!isProp)//property not found?
                    {
                        reader.Skip();//cant do anything with it...keep going.
                        continue;
                    }

                    #region property set
                    prop = piList.Where(a => a.Name.Equals(reader.Name)).Single();
                    if (prop.CanWrite)
                    {
                        if (prop.PropertyType.BaseType.IsGenericType)
                        {//this should be one of the Rig Collections, are there any false positives?
                            ReadXmlCollection(prop, reader);
                        }
                        else if (prop.PropertyType.IsSubclassOf(typeof(BusinessObjectBase)))
                        {
                            //have to create a 'new' object, then call its reader to fill it in, then set the property value.
                            object newObj = Activator.CreateInstance(prop.PropertyType, false);
                            prop.PropertyType.GetMethod("ReadXml").Invoke(newObj, new object[] { reader });
                            prop.SetValue(this, newObj, null);
                        }
                        else
                        {
                            string elVal = reader.ReadString();
                            if (!String.IsNullOrEmpty(elVal))
                            {
                                if (prop.PropertyType == typeof(String))
                                    prop.SetValue(this, elVal, null);
                                else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
                                    prop.SetValue(this, Convert.ToInt32(elVal), null);
                                else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
                                    prop.SetValue(this, Convert.ToDouble(elVal), null);
                                else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
                                    prop.SetValue(this, Convert.ToBoolean(elVal), null);
                                else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
                                {//special handle
                                    elVal = System.Text.RegularExpressions.Regex.Replace(elVal, @"(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}\.?\d*)([\+|-]\d{2}:\d{2})", "$1 $2");
                                    prop.SetValue(this, Convert.ToDateTime(elVal), null);
                                }
                            }
                        }
                    }
                    #endregion
                }

                if (prop == null)//read anyway, something weird has happened?
                {
                    if (!reader.Read())
                        break;
                }
                else if(prop.Name.Equals(reader.Name))//only read if we are still on this node, otherwise, this has already been done.
                    if (!reader.Read())
                        break;

                prop = null;
                isProp = false;
            }
        }
        reader.ReadEndElement();
    }

    private void ReadXmlCollection(PropertyInfo prop, System.Xml.XmlReader reader)
    {
        string colName = reader.Name;
        object collectionIntance = Activator.CreateInstance(prop.PropertyType, false);
        reader.ReadStartElement();
        //now we are at the first element in the collection...what is it?
        Type ColType = prop.PropertyType.BaseType.GetGenericArguments()[0];
        while (reader.Name != colName && reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            object colObj = Activator.CreateInstance(ColType, false);
            ColType.GetMethod("ReadXml").Invoke(colObj, new object[] { reader });
            prop.PropertyType.GetMethod("Add", new Type[] {ColType}).Invoke(collectionIntance, new object[] { colObj });
        }
        prop.SetValue(this, collectionIntance, null);
        reader.ReadEndElement();
    }

我认为它有效......但它也很慢。任何建议将不胜感激。

我也不介意一个大概的“好主,那是疯了,永远不会工作”......或类似的东西。请记住,这是在一个抽象基类上实现的,它在已经扩展的类型上运行。

这是我需要阅读的数据样式的一个极其截断且大规模简化的示例:

<RigX>
    <isDirty>True</isDirty>
    <RXJob>
        <prop1>1.3</prop1>
        <prop2>False<prop2>
        <prop3>1984-08-10T09:30:00-06:00</prop3>
        <prop4 />
        <prop5>Hello World</prop5>
    </RXJob>
    <RXCollection>
        <ColObject>
            <p1>Yo</p1>
            <p2>Yellow</p2>
        </ColObject>
        <ColObject>
            <p1>No</p1>
            <p2>Orange</p2>
        </ColObject>
    </RXCollection>
    <RXNestedCollection>
        <RXNestedObject>
            <RXAnotherCollection>
                <RXAnotherObject>
                    <p1>1.9</p1>
                </RXAnotherObject>
            </RXAnotherCollection>
            <RXObject>
                <prop1>nope</prop1>
                <prop2>Im On A Horse!</prop2>
            </RXObject>
        </RXNestedObject>
    </RXNestedCollection>
</RigX>

在此示例中,RigX是外部的单个对象。 IsDirty是RigX的财产。同样,RXJob,RXCollection和RXNestedCollection是RigX的属性。 RXJob是一个对象,另外两个是集合。 RXCollection是一个包含未知数量的用户定义对象的集合,每个对象都是一个只包含灵长类动物的简单对象。 RXNestedCollection包含未知数量的用户定义对象,每个对象包含其他用户定义的对象和集合。

0 个答案:

没有答案