XML 属性未获取命名空间前缀

Posted

技术标签:

【中文标题】XML 属性未获取命名空间前缀【英文标题】:XML attribute not getting namespace prefix 【发布时间】: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>

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

【问题讨论】:

【参考方案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 方法之外我不需要实现任何东西。

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

【讨论】:

以上是关于XML 属性未获取命名空间前缀的主要内容,如果未能解决你的问题,请参考以下文章

JAXB:为啥在生成的 xml 文档中未使用定义的命名空间前缀?

XML:一个命名空间前缀被声明为未声明,而事实上它是

如何用前缀替换 xmlns 命名空间属性?

如何在 .NET 中的反序列化期间指定 XML 序列化属性以支持命名空间前缀?

带有命名空间前缀的 TagSoup 属性

如何使用 lxml 在属性值中设置命名空间前缀?