I have xml with several items, for example:
<TestObject>
<TestElement1/>
<TestElement2/>
</TestObject>
<TestObject>
<TestElement1/>
<TestElement2/>
</TestObject>
Also I have class:
class TestClass {
public int TestElement1 { get; set; }
public int Element { get; set; }
}
If I do:
XmlSerializer s = new XmlSerializer(typeof(List<TestClass>));
List<TestClass> list = (List<TestClass>)s.Deserialize("myXml.xml");
After it I get list with objects TestClass, but property Element didn't set. How I must change serialization, if I want to set TestElement2 in Element field?
You need to decorate the Element property with an [XmlElement] attribute:
[XmlRoot("TestObject")]
class TestClass {
public int TestElement1 { get; set; }
[XmlElement("TestElement2")]
public int Element { get; set; }
}
Try XmlElement attribute
public class TaxRates{
[XmlElement(ElementName = "TaxRate")]
public decimal ReturnTaxRate;
}
see Controlling XML Serialization Using Attributes
Related
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?
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. From testing, this appears to happen when the attribute 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
<q1:IncidentEvent q1:EventTypeText="an attribute" xmlns:q1="http://foo" />
as required.
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
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
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.
i have part of xml document
<Tender SubTenderType="BC" TenderType="Check">
<TenderTotal>
<Amount>10.00</Amount>
</TenderTotal>
</Tender>
i need convert it to class.
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
what i already wrote and this work. but i interseing can i deserialize xml to class as written above?
[Serializable]
public class Tender
{
[XmlAttribute("SubTenderType")]
public string SubTenderType { get; set; }
[XmlAttribute("TenderType")]
public string TenderType { get; set; }
[XmlElement("TenderTotal")]
public TenderTotal TenderTotal { get; set; }
}
[Serializable]
public class TenderTotal
{
[XmlElement("Amount")]
public decimal Amount { get; set; }
}
You can deserialize xml to first Type "Tender" and next use autoMapper to map your type (create new object of different type)
create map:
config.CreateMap<TenderFirst, TenderSecond>().ForMember(x => x.TenderTotal.Amount, y => y.Amount ())
Having the following class without XmlAttribute:
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
You can use the XmlAttributeOverrides class to override the behavior of the serializer in such a way that instead of elements it would do the attributes.
var attrSTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("SubTenderType") };
var attrTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("TenderType") };
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Tender), nameof(Tender.SubTenderType), attrSTT);
overrides.Add(typeof(Tender), nameof(Tender.TenderType), attrTT);
var xs = new XmlSerializer(typeof(Tender), overrides);
However, in this way impossible add a new item or wrap one element in another.
Therefore, you have to do custom serialization, or map one type to another, or writing a custom xml reader/writer, or perform the read/write manually (for example, using linq2xml). There are many ways...
I'm trying to deserialize an XML into a C# object that has numerous elements of the same type. I've pared down the contents for clarity. My C# class looks like this:
[XmlInclude(typeof(RootElement))]
[XmlInclude(typeof(Entry))]
[Serializable, XmlRoot("Form")]
public class DeserializedClass
{
public List<Entry> listEntry;
public RootElement rootElement { get; set; }
}
Then I define the Entry and RootElement classes as follows:
public class RootElement
{
public string rootElementValue1 { get; set; }
public string rootElementValue2 { get; set; }
}
public class Entry
{
public string entryValue1 { get; set; }
public string entryValue2 { get; set; }
}
And the structure of the XML I'm trying to deserialize looks like this:
<Entry property="value">
<entryValue1>Data 1</entryValue1>
<entryValue2>Data 2</entryValue2>
<RootElement>
<rootElementValue1>Data 3</rootElementValue1>
<rootElementValue2>Data 4</rootElementValue2>
</RootElement>
<RootElement>
<rootElementValue1>Data 5</rootElementValue1>
<rootElementValue2>Data 6</rootElementValue2>
</RootElement>
</Entry>
As you can see there will be multiple RootElement elements that I want to deserialize into the List of the C# object. To deserialize I use the following:
XmlSerializer serializer = new XmlSerializer(typeof(DeserializedClass));
using (StringReader reader = new StringReader(xml))
{
DeserializedClass deserialized = (DeserializedClass)serializer.Deserialize(reader);
return deserialized;
}
Any ideas how to fix it?
I tweaked your classes a little bit for your deserialization code to work:
[Serializable, XmlRoot("Entry")]
public class DeserializedClass
{
public string entryValue1;
public string entryValue2;
[XmlElement("RootElement")]
public List<RootElement> rootElement { get; set; }
}
public class RootElement
{
public string rootElementValue1 { get; set; }
public string rootElementValue2 { get; set; }
}
Now it works fine.
I don't know why you declared your XmlRoot as "Form" as there is no element in the XML with that name so I replaced it with "Entry".
You cannot use an Entry class with entryvalue1 and entryvalue2 properties because they are direct children of the root (Event) and there is no child as Entry. In short your classes must reflect the hierarchy of the XML so that deserialization can work properly.
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?
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. From testing, this appears to happen when the attribute 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
<q1:IncidentEvent q1:EventTypeText="an attribute" xmlns:q1="http://foo" />
as required.
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
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
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.
I found something that confused me today:
1. If I have this:
public interface INamed
{
[XmlAttribute]
string Name { get; set; }
}
public class Named : INamed
{
public string Name { get; set; }
}
It gives the following output (Name property serialized as element):
<Named>
<Name>Johan</Name>
</Named>
2. If I have this:
public abstract class NamedBase
{
[XmlAttribute]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
public override string Name { get; set; }
}
The XmlSerializer throws System.InvalidOperationException
Member 'NamedDerived.Name' hides inherited member 'NamedBase.Name',
but has different custom attributes.
The code I used for serialization:
[TestFixture]
public class XmlAttributeTest
{
[Test]
public void SerializeTest()
{
var named = new NamedDerived {Name = "Johan"};
var xmlSerializer = new XmlSerializer(named.GetType());
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, named);
}
Console.WriteLine(stringBuilder.ToString());
}
}
My question is:
Am I doing it wrong and if so what is the correct way to use xml attributes in interfaces and abstract classes?
Attributes are not inherited on overriden properties. You need to redeclare them.
Also in your first example the behavior is not the "expected" one as you declared XmlAttribute at the interface level and yet the serialized xml contains the value as an element. So the attribute in the interface is ignored and only info taken from the actual class matters.
I think you should xmlignore your abstract class property
public abstract class NamedBase
{
[XmlIgnore]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
[XmlAttribute]
public override string Name { get; set; }
}