【问题标题】:c# xml deserialization to object with colon and hyphen in xsi:type valuec# xml 反序列化为 xsi:type 值中带有冒号和连字符的对象
【发布时间】:2018-02-24 19:03:57
【问题描述】:

当我尝试使用 XmlSerializer 类将我的 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'>.

我的反序列化器类如下所示:

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 文件的反序列​​化元素和属性的类如下所示:

[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"

【问题讨论】:

  • fxNameSpacehoNameSpace 是什么?
  • 相关或重复:xsi:type attribute messing up C# XML deserialization。该答案是否提供了足够的信息?你是如何生成你的 c# 类的?手动,还是通过一些代码生成工具?
  • fxnamespace 和 honamespace 只是我用于 XML 中的命名空间的变量。
  • 不,这个答案对我的类型没有帮助,看起来像这个 ethernet:CLUSTER-TYPE,我不能创建一个像这个字符串这样的类,因为它里面有特殊字符。所以我的问题是如何处理这种类型?我手动创建了这些类。
  • @Daniel - 很高兴minimal reproducible example 你的第一个问题。在 cmets 中,您可以使用@ 加上帐户名称来回复特定评论员,请参阅How do comment @replies work?。早些时候,我认为您试图回复我,但我从未收到通知。

标签: c# xml deserialization xmlserializer


【解决方案1】:

您的问题如下。 xsi:type 属性是{http://www.w3.org/2001/XMLSchema-instance}type 的缩写,是一个w3c standard attribute,它允许元素显式声明其类型,例如当它是预期元素类型的多态子类型时。 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,其中 : 之前的部分是范围内有效命名空间的 XML 命名空间前缀,之后的部分对应于 XmlTypeAttribute.TypeName 的多态类型。您可以通过将[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:生成 XML 文档时出错。 ---> System.InvalidOperationException:Fibextoobject+ClusterType 类型不是预期的。使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型。

    由于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

【讨论】:

  • 感谢您提供这个非常好的和翔实的答案。这解决了我的问题!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-24
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多