如何将具有不同名称但具有相同属性集的xml元素反序列化为类型化数组/集合

时间:2015-06-18 08:58:21

标签: c# xml serialization deserialization xml-deserialization

这是我试图反序列化的XML文件的一部分:

<entry>
...
<A:family type="user">
 <A:variationCount>7</A:variationCount>
 <A:part type="user">
    <title>94 LPS</title>
    <Voltage type="custom" typeOfParameter="Electrical Potential" units="V">120 V</Voltage>
    <Unit_Length displayName="Unit Length" type="custom" typeOfParameter="Length" units="mm">540</Unit_Length>
    <Unit_Height displayName="Unit Height" type="custom" typeOfParameter="Length" units="mm">222</Unit_Height>
    <Total_Cooling_Capacity displayName="Total Cooling Capacity" type="custom" typeOfParameter="Power" units="W">1758 W</Total_Cooling_Capacity>
    <Supply_Air_Width displayName="Supply Air Width" type="custom" typeOfParameter="Duct Size" units="mm">400 mm</Supply_Air_Width>
    <Supply_Air_Height displayName="Supply Air Height" type="custom" typeOfParameter="Duct Size" units="mm">150 mm</Supply_Air_Height>
    <Sensible_Cooling_Capacity displayName="Sensible Cooling Capacity" type="custom" typeOfParameter="Power" units="W">1348 W</Sensible_Cooling_Capacity>
    <Return_Air_Width displayName="Return Air Width" type="custom" typeOfParameter="Duct Size" units="mm">475 mm</Return_Air_Width>
    <Number_of_Poles displayName="Number of Poles" type="custom" typeOfParameter="Number of Poles">1</Number_of_Poles>
    <Load_Classification displayName="Load Classification" type="custom" typeOfParameter="Load Classification">Cooling</Load_Classification>
    <CFU_Material displayName="CFU Material" type="custom" typeOfParameter="Material">&lt;By Category&gt;</CFU_Material>
    <C2_Offset_1 displayName="C2 Offset 1" type="custom" typeOfParameter="Length" units="mm">108</C2_Offset_1>
    <C1_Offset_1 displayName="C1 Offset 1" type="custom" typeOfParameter="Length" units="mm">108</C1_Offset_1>
    <Apparent_Load displayName="Apparent Load" type="custom" typeOfParameter="Apparent Power" units="VA">50 VA</Apparent_Load>
 </A:part>
 <A:part type="user">
    ...
 </A:part>
 ...
</A:family>
</entry>

这些是我用来反序列化它的类:

[XmlType(AnonymousType = true)]
[XmlRoot("entry", Namespace="http://www.w3.org/2005/Atom")]
public class PartAtom
{
    ...
    [XmlElement("family", Namespace="urn:schemas-autodesk-com:partatom")]
    public Family Family { get; set; }
}

public class Family
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlElement("variationCount")]
    public int VariationCount { get; set; }

    [XmlElement("part")]
    public FamilyType[] Parts { get; set; }
}

[XmlRoot(Namespace = "http://www.w3.org/2005/Atom")]
public class FamilyType 
{   
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlElement("title")]
    public string Title { get; set; }

    [XmlArray]
    public Parameter[] Parameters { get; set; }
}

[XmlRoot(Namespace = "http://www.w3.org/2005/Atom")]
public struct Parameter
{
    [XmlAttribute("displayName")]
    public string Name { get; set; }

    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("typeOfParameter")]
    public string DataType { get; set; }

    [XmlText]
    public string Value { get; set; }

    [XmlAttribute("units")]
    public string Units { get; set; }
}

我希望将Voltage,Units_Length,Unit_Height,... Apparent_Load等元素反序列化为Parameters类的实例。 我怎么能做到这样的事情?它甚至可能吗?

更新

参数(标题下面的XML元素)实际上是无限的,所以我无法考虑所有这些参数,因此我必须手动指定XmlElement名称的所有算法都不可用。

3 个答案:

答案 0 :(得分:1)

如果您不想在您的课程上实施IXmlSerializable,则可以选择添加XElement []元素的代理属性标记为[XmlAnyElement],并序列化参数并根据需要修改名称。

假设您的XML在根元素上具有名称空间声明,如下所示:

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:A="urn:schemas-autodesk-com:partatom">

然后以下内容应该有效:

[XmlType(Namespace = "http://www.w3.org/2005/Atom")]
public class FamilyType
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlElement("title")]
    public string Title { get; set; }

    [XmlIgnore]
    public Parameter[] Parameters { get; set; }

    [XmlAnyElement]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public XElement[] XmlParameters
    {
        get
        {
            return XmlKeyValueListHelper.SerializeAttributeNameValueList(Parameters, "name");
        }
        set
        {
            Parameters = XmlKeyValueListHelper.DeserializeAttributeNameValueList<Parameter>(value, "name");
        }
    }
}

请注意,序列化程序会自动反序列化TitleType属性,而不是传递给AnyElement数组。然后,在Parameter中,我将Name更改为DisplayName并添加了ElementName属性来保存元素名称:

[XmlRoot(Namespace = "http://www.w3.org/2005/Atom")]
public struct Parameter
{
    [XmlAttribute("name")]
    public string ElementName { get; set; } // Added property.

    [XmlAttribute("displayName")]
    public string DisplayName { get; set; } // Changed from Name to DisplayName

    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("typeOfParameter")]
    public string DataType { get; set; }

    [XmlText]
    public string Value { get; set; }

    [XmlAttribute("units")]
    public string Units { get; set; }
}

使用扩展和辅助方法:

public static class XmlKeyValueListHelper
{
    public static XElement[] SerializeAttributeNameValueList<T>(IEnumerable<T> items, string nameAttributeName)
    {
        if (items == null)
            return null;
        var ns = new XmlSerializerNamespaces();
        ns.Add("", typeof(T).RootXmlElementNamespace());
        var query = items
            .Select(p => p.SerializeToXElement(ns))
            .Select(e =>
            {
                var attr = e.Attribute(nameAttributeName);
                e.Name = e.Name.Namespace + XmlConvert.EncodeLocalName((string)attr);
                attr.Remove();
                return e;
            });
        return query.ToArray();
    }

    public static T[] DeserializeAttributeNameValueList<T>(IEnumerable<XElement> elements, string nameAttributeName)
    {
        if (elements == null)
            return null;
        var query = elements
            .Select(e => new XElement(e)) // Do not modify the input values.
            .Select(e =>
            {
                e.Add(new XAttribute(nameAttributeName, XmlConvert.DecodeName(e.Name.LocalName)));
                e.Name = e.Name.Namespace + typeof(T).RootXmlElementName();
                return e;
            })
            .Select(e => e.Deserialize<T>());
        return query.ToArray();
    }
}

public static class XmlTypeExtensions
{
    public static string RootXmlElementName(this Type type)
    {
        var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
        if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.ElementName))
            return xmlRoot.ElementName;
        return type.Name;
    }

    public static string RootXmlElementNamespace(this Type type)
    {
        var xmlRoot = type.GetCustomAttribute<XmlRootAttribute>();
        if (xmlRoot != null && !string.IsNullOrEmpty(xmlRoot.Namespace))
            return xmlRoot.Namespace;
        return string.Empty;
    }
}

public static class XObjectExtensions
{
    static XmlSerializerNamespaces NoStandardXmlNamespaces()
    {
        var ns = new XmlSerializerNamespaces();
        ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
        return ns;
    }

    public static XElement SerializeToXElement<T>(this T obj)
    {
        return obj.SerializeToXElement(null, NoStandardXmlNamespaces());
    }

    public static XElement SerializeToXElement<T>(this T obj, XmlSerializerNamespaces ns)
    {
        return obj.SerializeToXElement(null, ns);
    }

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

    public static T Deserialize<T>(this XContainer element)
    {
        return element.Deserialize<T>(new XmlSerializer(typeof(T)));
    }

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

仍然比自定义IXmlSerializable更容易。

答案 1 :(得分:0)

执行此操作的一种(丑陋)方式可能是在对其进行反序列化之前修改XML。只需将所有“Voltage”,“Unit_height”等字符串替换为“YourParameterClassName”。然后他们应该很好地反序列化,并且我猜你的“typeOfParameter”仍将为你提供在替换之前最初存在于节点中的相同信息。

答案 2 :(得分:0)

一个解决方案可能是指定了不同名称的多个XmlElementAttribute。如下所示:

 [XmlElement("Voltage")]
 [XmlElement("Unit_Height")]
 [XmlElement("Supply_Air_Height")]
 [XmlElement("CFU_Material")]