4

I need to generate the following XML during serialization: (fragment)

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

The class in question looks like this:

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

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

}

It appears that the serializer is noticing that the namespace is already declared in an xmlns: at the root and is ignoring my attribute. I also tried the following:

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

}

This results in the following 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>

Which is not what I want. The element shouldn't be prefixed, the attribute should be. What is needed to get the serializer to understand what I want?

4 Answers 4

12

This may be a bug in XmlSerializer.

As you have noticed, even when XmlAttributeAttribute.Namespace is set explicitly, the attribute will not be prefixed in certain situations when it should be. From testing, this appears to happen when the attribute's required namespace happens to be the same as the namespace of the element currently being written.

For instance:

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

Serializes to the following XML:

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

And since the attribute is unprefixed, it's actually not in any namespace, as is explained in the XML standard: The namespace name for an unprefixed attribute name always has no value.

However, the following:

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

Serializes with the attribute correctly prefixed:

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

The workaround is to explicitly set [XmlAttribute(Form = XmlSchemaForm.Qualified)]. Thus:

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

Serializes to

<IncidentEvent xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  p3:EventTypeText="Beginning" xmlns:p3="http://foo" xmlns="http://foo" />

As required. Demo .NET Framework fiddle here.

Sign up to request clarification or add additional context in comments.

3 Comments

But the OP explicitly does not want to prefix the element (q1:IncidentEvent)?
@GSerg - I found the code I used for testing this 9 years ago. Turns out I had disabled writing of the standard xsi and xsd namespaces which had the side-effect of also disabling the default root namespace -- which caused everything to be prefixed. So I recreated the code without that, and you can see that the attribute is prefixed while the element is not -- with both of them being in the "http://foo" namespace. I've added a fiddle demonstrating this.
@GSerg - remember also that the value of the namespace prefix isn't semantically relevant, it's just a lookup into the namespaces in scope. It's the actual namespace that is semantically relevant. So the presence or absence of a prefix on an element might not be meaningful, you have to know the current default namespace -- it might be the same as the namespace corresponding to the prefix. But the presence or absence of a prefix on an attribute is more meaningful because an attribute without a prefix is hardcoded to be in the empty namespace rather than the default namespace in scope.
1

I did some research may be following answer helps

For Attributes to have namespace prefix you have to specify a different namespace tag other than what you have specified http://foo. Following code hopefully will solve your issue. In the code i have remove the namespace for elements and added only for the attribute.

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

1 Comment

This seems to fall apart if you need to serialize this object as a child of another object. It worked fine when I tested it in one scenario, but I'd forgotten about the other. Still, good answer.
0

Namespaces are meant to differentiate between two XML element having same name. As different XML elements can have same attribute name but different meaning. So there is no advantage of having namespace tag for an attribute as XML attributes are considered to be part of "element namespace" only. In your example

<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 is part of namespace ett:IncidentEvent Please refer to http://www.w3.org/TR/REC-xml-names/ for XML Namespaces

1 Comment

The XML needs to validate against an XSD that I don't have control over. All I need to do is write XML that conforms to the schema. My first fragment is hand written, but it does validate. I just need to figure out how to get the serializer to do the same.
0

I'll give KKD credit for the answer, but I discovered another scenario that still causes issues. Apparently if the object to be serialized is a child of another object, if the parent object's namespace is the same as the child's, the serializer assumes you don't need to explicitly declare the namespace for the child.

 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);
    }
 }

By implementing IXmlSerializable, I can manually write out the elements and attributes in exactly the way I need. Since this is a one-way export, I didn't need to implement anything but the WriteXml method.

I'm still not sure if this is the best way, but it works for the moment.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.