XML属性没有获取名称空间前缀

时间:2013-12-18 18:47:08

标签: c# xml xml-serialization xmlserializer

我需要在序列化期间生成以下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:root中声明,并且忽略了我的属性。我也尝试了以下内容:

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

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

4 个答案:

答案 0 :(得分:5)

这可能是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" />

根据需要。

答案 1 :(得分:1)

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

对于具有名称空间前缀的属性,您必须指定除指定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

答案 2 :(得分:0)

命名空间用于区分具有相同名称的两个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是namespace ett:IncidentEvent的一部分 有关XML命名空间的详细信息,请参阅http://www.w3.org/TR/REC-xml-names/

答案 3 :(得分:0)

我会给出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方法之外的任何东西。

我仍然不确定这是否是最佳方式,但它现在有效。