【问题标题】:XML attribute not getting namespace prefixXML 属性未获取命名空间前缀
【发布时间】:2021-12-14 00:41:24
【问题描述】:

我需要在序列化过程中生成以下 XML: (片段)

<IncidentEvent a:EventTypeText="Beginning" xmlns:a="http://foo">
  <EventDate>2013-12-18</EventDate>
  <EventTime>00:15:28</EventTime>
</IncidentEvent>

有问题的类如下所示:

public class IncidentEvent
{
    public string EventDate { get; set; }
    public string EventTime { get; set; }

    [XmlAttribute("EventTypeText", Namespace = "http://foo")]
    public string EventTypeText { get; set; }

}

似乎序列化程序注意到名称空间已在 xmlns: 根中声明,并且忽略了我的属性。我还尝试了以下方法:

[XmlRoot(Namespace = "http://foo")]
public class IncidentEvent
{
    public string EventDate { get; set; }
    public string EventTime { get; set; }

    private XmlSerializerNamespaces _Xmlns;

    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Xmlns
    {
        get
        {
            if (_Xmlns == null)
            {
                _Xmlns = new XmlSerializerNamespaces();
                _Xmlns.Add("ett", "http://foo");
            }

            return _Xmlns;
        }

        set
        {
            _Xmlns = value;
        }
    }


    [XmlAttribute("EventTypeText", Namespace = "http://foo")]
    public string EventTypeText { get; set; }

}

这会产生以下 XML:

  <ett:IncidentEvent EventTypeText="Beginning" xmlns:ett="http://foo">
    <ett:EventDate>2013-12-18</ett:EventDate>
    <ett:EventTime>00:15:28</ett:EventTime>
  </ett:IncidentEvent>

这不是我想要的。元素不应该是前缀,属性应该是。需要什么才能让序列化程序了解我想要什么?

【问题讨论】:

    标签: c# xml xml-serialization xmlserializer


    【解决方案1】:

    这可能是XmlSerializer 中的一个错误。

    正如您所注意到的,即使XmlAttributeAttribute.Namespace 被显式设置,在某些情况下该属性也不会作为前缀。根据测试,当属性命名空间恰好与当前正在写入的元素的命名空间相同时,似乎会发生这种情况。

    例如:

    [XmlRoot(Namespace = "http://foo")]
    public class IncidentEvent
    {
        [XmlAttribute("EventTypeText", Namespace = "http://foo")]
        public string EventTypeText { get; set; }
    }
    

    序列化为以下 XML:

    <q1:IncidentEvent EventTypeText="an attribute" xmlns:q1="http://foo" />
    

    由于属性没有前缀,它实际上不在任何命名空间中,正如in the XML standard 所解释的那样:无前缀属性名称的命名空间名称始终没有值

    但是,以下内容:

    [XmlRoot(Namespace = "http://foo")]
    public class IncidentEvent
    {
        [XmlAttribute("EventTypeText", Namespace = "http://bar")]
        public string EventTypeText { get; set; }
    }
    

    使用正确前缀的属性进行序列化:

    <q1:IncidentEvent p1:EventTypeText="an attribute" xmlns:p1="http://bar" xmlns:q1="http://foo" />
    

    解决方法是显式设置[XmlAttribute(Form = XmlSchemaForm.Qualified)]。因此:

    [XmlRoot(Namespace = "http://foo")]
    public class IncidentEvent
    {
        [XmlAttribute("EventTypeText", Namespace = "http://foo", Form = XmlSchemaForm.Qualified)]
        public string EventTypeText { get; set; }
    }
    

    序列化为

    <q1:IncidentEvent q1:EventTypeText="an attribute" xmlns:q1="http://foo" />
    

    根据需要。

    【讨论】:

      【解决方案2】:

      我做了一些研究可能会得到以下答案的帮助

      要使属性具有命名空间前缀,您必须指定与指定 http://foo 不同的命名空间标签。以下代码有望解决您的问题。在代码中,我删除了元素的命名空间并仅添加了属性。

      public class IncidentEvent
      {
          public string EventDate { get; set; }
          public string EventTime { get; set; }
      
          [XmlAttribute("EventTypeText", Namespace = "http://foo")]
          public string EventTypeText { get; set; }
      
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              IncidentEvent xmlObj = new IncidentEvent()
              {
                  EventDate = "2012.12.01",
                  EventTime = "1:00:00",
                  EventTypeText = "Beginining"
              };
              XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
              ns.Add("ett", "http://foo");
              XmlSerializer serializer = new XmlSerializer(typeof(IncidentEvent));
              serializer.Serialize(Console.OpenStandardOutput(), xmlObj, ns);
              Console.WriteLine();
          }
      }
      

      http://www.w3.org/TR/2009/REC-xml-names-20091208/#defaulting

      【讨论】:

      • 如果您需要将此对象序列化为另一个对象的子对象,这似乎会崩溃。当我在一种情况下对其进行测试时它运行良好,但我忘记了另一种情况。不过,很好的答案。
      【解决方案3】:

      命名空间旨在区分具有相同名称的两个 XML 元素。由于不同的 XML 元素可以具有相同的属性名称但含义不同。因此,属性的名称空间标记没有任何优势,因为 XML 属性仅被视为“元素名称空间”的一部分。 在你的例子中

      <ett:IncidentEvent EventTypeText="Beginning" xmlns:ett="http://foo">
          <ett:EventDate>2013-12-18</ett:EventDate>
          <ett:EventTime>00:15:28</ett:EventTime>
      </ett:IncidentEvent>
      

      EventTypeText 是命名空间 ett:IncidentEvent 的一部分 XML 命名空间请参考http://www.w3.org/TR/REC-xml-names/

      【讨论】:

      • XML 需要针对我无法控制的 XSD 进行验证。我需要做的就是编写符合架构的 XML。我的第一个片段是手写的,但它确实有效。我只需要弄清楚如何让序列化程序做同样的事情。
      【解决方案4】:

      我将把答案归功于 KKD,但我发现了另一种仍然会导致问题的场景。显然,如果要序列化的对象是另一个对象的子对象,如果父对象的命名空间与子对象的命名空间相同,则序列化程序假定您不需要显式声明子对象的命名空间。

       public class IncidentEvent : IXmlSerializable
       {
          public string EventDate { get; set; }
          public string EventTime { get; set; }
          public string EventTypeText { get; set; }
      
          public System.Xml.Schema.XmlSchema GetSchema()
          {
              return null;
          }
      
          public void ReadXml(System.Xml.XmlReader reader)
          {
              return null;
          }
      
          public void WriteXml(System.Xml.XmlWriter writer)
          {
              writer.WriteAttributeString("ex", "EventTypeText", "http://foo", EventTypeText);
          }
       }
      

      通过实现 IXmlSerializable,我可以完全按照我需要的方式手动写出元素和属性。由于这是一种单向导出,因此除了 WriteXml 方法之外我不需要实现任何东西。

      我仍然不确定这是否是最好的方式,但它目前有效。

      【讨论】:

        猜你喜欢
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-24
        相关资源
        最近更新 更多