c# xml 反序列化到 xsi:type 值中带有冒号和连字符的对象

c# xml deserialization to object with colon and hyphen in xsi:type value

当我尝试使用 XmlSerializer class.

将我的 XML 文件反序列化为对象时遇到问题

我的 XML 文件如下所示:

<fx:FIBEX xmlns:fx="http://www.asam.net/xml/fbx" xmlns:ho="http://www.asam.net/xml" xmlns:ethernet="http://www.asam.net/xml/fbx/ethernet" xmlns:it="http://www.asam.net/xml/fbx/it" xmlns:service="http://www.asam.net/xml/fbx/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="" VERSION="4.1.0">
  <fx:ELEMENTS>
    <fx:CLUSTERS>
      <fx:CLUSTER xsi:type="ethernet:CLUSTER-TYPE" ID="ID_CLUSTER_MAIN_1">
        <ho:SHORT-NAME>SomeIpDatabase</ho:SHORT-NAME>
        <fx:SPEED>1000000000</fx:SPEED>
        <fx:IS-HIGH-LOW-BIT-ORDER>false</fx:IS-HIGH-LOW-BIT-ORDER>
        <fx:BIT-COUNTING-POLICY>SAWTOOTH</fx:BIT-COUNTING-POLICY>
        <fx:PROTOCOL>ETHERNET</fx:PROTOCOL>
        <fx:PHYSICAL>OABR</fx:PHYSICAL>
        <fx:CHANNEL-REFS>
          <fx:CHANNEL-REF ID-REF="ID_CHANNEL_SOME_IP_1" />
        </fx:CHANNEL-REFS>
        <fx:MAX-FRAME-LENGTH>1500</fx:MAX-FRAME-LENGTH>
        <ethernet:MAC-MULTICAST-GROUPS>
          <ethernet:MAC-MULTICAST-GROUP ID="ID_CLUSTER_MAIN_1_ID_MACMULTICASTGROUP_SD_1">
            <ho:SHORT-NAME>SD</ho:SHORT-NAME>
            <ethernet:MAC-MULTICAST-ADDRESS>01:00:5E:40:FF:FB</ethernet:MAC-MULTICAST-ADDRESS>
          </ethernet:MAC-MULTICAST-GROUP>
          <ethernet:MAC-MULTICAST-GROUP ID="ID_CLUSTER_MAIN_1_ID_MACMULTICASTGROUP_BROADCAST_1">
            <ho:SHORT-NAME>BROADCAST</ho:SHORT-NAME>
            <ethernet:MAC-MULTICAST-ADDRESS>FF:FF:FF:FF:FF:FF</ethernet:MAC-MULTICAST-ADDRESS>
          </ethernet:MAC-MULTICAST-GROUP>
        </ethernet:MAC-MULTICAST-GROUPS>
      </fx:CLUSTER>
      <!--Additional CLUSTER elements omitted-->
    </fx:CLUSTERS>
  </fx:ELEMENTS>
  <!--PROJECT elements omitted-->
</fx:FIBEX>

当我现在尝试反序列化 XML 文件时,我收到以下错误:

System.InvalidOperationException: Error in XML-Dokument (11,5). ---> System.InvalidOperationException: The specified type was not recognized: Name='CLUSTER-TYPE', Namespace='http://www.asam.net/xml/fbx/ethernet', at <CLUSTER xmlns='http://www.asam.net/xml/fbx'>.

我的反序列化器 class 看起来像这样:

public static T DeserializeXMLFileToObject<T>(string XmlFilename)
{
    T returnObject = default(T);
    if (string.IsNullOrEmpty(XmlFilename)) return default(T);

    try
    {
        StreamReader xmlStream = new StreamReader(XmlFilename);
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        returnObject = (T)serializer.Deserialize(xmlStream);
    }
    catch (Exception ex)
    {
        Console.Write("{1} Es ist ein Fehler aufgetreten {0}", ex, DateTime.Now);
    }
    return returnObject;
}

应该包含 XML 文件的反序列​​化元素和属性的 class 如下所示:

[XmlRoot("FIBEX", Namespace = fxNameSpace)]
public class Fibextoobject
{
    [XmlElement("PROJECT", Namespace = fxNameSpace)]
    public Project project { get; set; }

    [XmlElement("ELEMENTS", Namespace = fxNameSpace)]
    public Elements elements { get; set; }

    public class Project
    {
        [XmlAttribute("OID", Namespace = hoNameSpace)]
        public string OID { get; set; }

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

        [XmlElement("SHORT-NAME", Namespace = hoNameSpace)]
        public string shortname { get; set; }

        [XmlElement("LONG-NAME", Namespace = hoNameSpace)]
        public string longname { get; set; }
    }

    public class Elements
    {
        [XmlArray("CLUSTERS", Namespace = fxNameSpace)]
        [XmlArrayItem("CLUSTER", Namespace = fxNameSpace)]
        public List<Cluster> cluster { get; set; }

    }

    public class Cluster
    {
        [XmlAttribute("ID")]
        public string ID { get; set; }

        [XmlElement("SHORT-NAME", Namespace = hoNameSpace)]
        public string shortname { get; set; }

        [XmlElement("SPEED", Namespace = fxNameSpace)]
        public string speed { get; set; }
    }
}

如何使用 xsi:type 属性成功反序列化 XML 文件,值中包含冒号和连字符:xsi:type="ethernet:CLUSTER-TYPE" ?

您的问题如下。 xsi:type 属性是 {http://www.w3.org/2001/XMLSchema-instance}type 的缩写,是一个 w3c standard attribute that allows an element to explicitly assert its type, e.g. when it is a polymorphic subtype of the expected element type. XmlSerializer supports this attribute 并且将使用它来确定对象的实际类型以反序列化这种多态类型。但是,使用此属性有几个注意事项和限制:

  1. 如果 xsi:type 出现在 XML 中,则该元素 必须 绑定到多态类型层次结构。 XmlSerializer 永远不会忽略该属性。因此,您需要引入 Cluster 的派生子类型来反序列化此 XML,例如如下:

    public class ClusterType : Cluster
    {
    }
    
  2. XmlSerializer 要求使用[XmlInclude(typeof(TDerivedType))] 提前通知 所有可能的子类型。通常将此属性放在基本类型上:

    [XmlInclude(typeof(ClusterType))]
    // Add XmlInclude for all additional subtypes here.
    public class Cluster
    {
        // Remainder unchanged
    
  3. 您的 xsi:type 值包含不能包含在 c# 标识符中的字符:

    xsi:type="ethernet:CLUSTER-TYPE"
    

    在这种情况下,XmlSerializer 将值解释为派生类型的 qualified name where the portion before the : is an XML namespace prefix of a valid namespace in scope, and the portion afterwards corresponds to the XmlTypeAttribute.TypeName of the polymorphic type. You can inform the serializer of the expected namespace and type by applying the [XmlType] 属性,如下所示:

    [XmlType("CLUSTER-TYPE", Namespace = ethernetNameSpace)]
    public class ClusterType : Cluster
    {
    }
    

    其中 ethernetNameSpace 定义为:

    public const string ethernetNameSpace = "http://www.asam.net/xml/fbx/ethernet";
    
  4. 出于某种原因,XmlSerializer 需要将 XmlTypeAttribute.Namespace 初始化为 基本类型 上的某些内容,如果它被设置为 所有 包含的派生类型。名称空间的值在反序列化派生类型的实例时似乎并不重要(尽管在反序列化基类型时显然会如此),它只需要设置为 something。即使是空字符串也可以,例如:

    // The XmlTypeAttribute.Namespace below must be initialized to something if it is also initialized on the derived type.
    // However, the actual value does not need to be the same as the child's namespace, so modify to be something more appropriate
    // based on additional XML samples.
    [XmlType("CLUSTER", Namespace = "")] 
    [XmlInclude(typeof(ClusterType))]
    // Add XmlInclude for all additional subtypes here.
    public class Cluster
    {
        // Remainder unchanged
    

    如果未设置基类型 XML 命名空间,XmlSerializer 将抛出异常并显示误导性消息:

    System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type Fibextoobject+ClusterType was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

    由于 ClusterType 实际上是通过 [XmlInclude] 包含的,因此该消息没有帮助。需要一些实验才能确定实际问题。

您的 Fibextoobject 类型的工作版本 如下所示:

[XmlRoot("FIBEX", Namespace = fxNameSpace)]
public partial class Fibextoobject
{
    [XmlElement("PROJECT", Namespace = fxNameSpace)]
    public Project project { get; set; }

    [XmlElement("ELEMENTS", Namespace = fxNameSpace)]
    public Elements elements { get; set; }

    public class Project
    {
        [XmlAttribute("OID", Namespace = hoNameSpace)]
        public string OID { get; set; }

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

        [XmlElement("SHORT-NAME", Namespace = hoNameSpace)]
        public string shortname { get; set; }

        [XmlElement("LONG-NAME", Namespace = hoNameSpace)]
        public string longname { get; set; }
    }

    public class Elements
    {
        [XmlArray("CLUSTERS", Namespace = fxNameSpace)]
        [XmlArrayItem("CLUSTER", Namespace = fxNameSpace)]
        public List<Cluster> cluster { get; set; }
    }

    [XmlType("CLUSTER-TYPE", Namespace = ethernetNameSpace)]
    public class ClusterType : Cluster
    {
    }

    // The XmlTypeAttribute.Namespace below must be initialized to something if it is also initialized on the derived type.
    // However, the actual value does not need to be the same as the child's namespace, so modify to be something more appropriate
    // based on additional XML samples.
    [XmlType("CLUSTER", Namespace = "")] 
    [XmlInclude(typeof(ClusterType))]
    // Add XmlInclude for all additional subtypes here.
    public class Cluster
    {
        [XmlAttribute("ID")]
        public string ID { get; set; }

        [XmlElement("SHORT-NAME", Namespace = hoNameSpace)]
        public string shortname { get; set; }

        [XmlElement("SPEED", Namespace = fxNameSpace)]
        public string speed { get; set; }
    }
}

public partial class Fibextoobject
{
    public const string fxNameSpace = "http://www.asam.net/xml/fbx";
    public const string hoNameSpace = "http://www.asam.net/xml";
    public const string ethernetNameSpace = "http://www.asam.net/xml/fbx/ethernet";
    public const string itNameSpace = "http://www.asam.net/xml/fbx/it";
    public const string serviceNameSpace = "http://www.asam.net/xml/fbx/services";
}

工作示例 Roslyn .Net fiddle