How to add Xml attribute to a property at runtime - c#

I need to serialize a class to xml. If a certain condition is met at run-time, I want to add an XML attribute to an element and assign it a value. Sometimes, the "Error" attribute will appear and sometimes it won't.
My code that serializes my objects:
public class XmlToolsRepo : IXmlTools
{
public string SerializeToXML<T>(object obj)
{
string results = null;
Encoding enc = Encoding.UTF8;
using (MemoryStream ms = new MemoryStream())
{
using (XmlTextWriter xw = new XmlTextWriter(ms, enc))
{
xw.Formatting = Formatting.None;
XmlSerializerNamespaces emptyNS = new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") });
XmlSerializer xSerializer = new XmlSerializer(typeof(T));
xSerializer.Serialize(xw, obj, emptyNS);
}
results = enc.GetString(ms.ToArray());
}
return results;
}
}
A class with a property that could have a new attribute at run-time:
[DataContract]
public class H204
{
[DataMember]
[XmlAttribute]
public string Code { get; set; }
[DataMember]
public string DW { get; set; }
}
When a condition is met I need for the XML to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<H204 Code="A">
<DW Error="test" />
</H204>

Try following :
public class H204
{
[XmlAttribute(AttributeName = "Code")]
public string Code { get; set; }
[XmlElement(ElementName = "DW")]
public DW dw{ get; set; }
}
public class DW
{
[XmlAttribute(AttributeName = "Error")]
public string text { get; set; }
}

Related

Deserialize XML with duplicate nested tags

I'm deserialization the results of a request that has the same tag repeated at multiple levels, I have it working but to do so I'm changing the format of the XML before attempting to deserialize it.
I'm not able to edit the source of the XML to change it to only have Diary at one level.
Is there a way to adjust my XML attributes to handle the deserialization without needing to adjust the response?
XML Response
<?xml version="1.0" ?>
<Root>
<DiaryDetails>
<Diary>
<Diary created_user="value1" created_date="value2" long_text="value3" short_text="value4" entry_type="value5" >Value6</Diary>
</Diary>
<Diary>
<Diary created_user="value7" created_date="value8" long_text="value9" short_text="value10" entry_type="value11" >Value12</Diary>
</Diary>
</DiaryDetails>
</Root>
Class definition
[XmlRoot("DiaryDetails")]
public class Diaries : List<Diary> { }
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
Deserialization Method
internal T DeserilaiseObject<T>(string response)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
T DeserilaisedObject;
using (TextReader reader = new StringReader(response))
{
DeserilaisedObject = (T)serializer.Deserialize(reader);
}
return DeserilaisedObject;
}
I'm currently handling this with a string replace:
response = response.Replace("<Diary><Diary", "<Diary").Replace("</Diary></Diary>", "</Diary>");
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication40
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
[XmlRoot("Root")]
public class Root
{
[XmlArray("DiaryDetails")]
[XmlArrayItem("Diary")]
public List<DiaryMain> diaries { get; set; }
}
public class DiaryMain
{
public Diary Diary { get; set; }
}
[XmlRoot("Diary")]
public class Diary
{
[XmlAttribute("created_user")]
public string CreatedBy { get; set; }
[XmlAttribute("created_date")]
public string CreatedDate { get; set; }
[XmlAttribute("long_text")]
public string LongText { get; set; }
[XmlAttribute("short_text")]
public string ShortText { get; set; }
[XmlAttribute("entry_type")]
public string Type { get; set; }
}
}
Assuming that you are only interested at the attributed Diary elements at the second nesting level you could do this:
// load your xml into a document
var doc = new XmlDocument();
doc.Load("your_xml_file.xml");
// extract the interesting nodes using xpath
var nodes = doc.SelectNodes("//Diary/Diary");
// deserialize the filtered NodeList (yes it's that clunky)
var serializer = new XmlSerializer(typeof(Diary));
var diaries = nodes.Cast<XmlNode>().Select(x => serializer.Deserialize(new XmlNodeReader(x))).Cast<Diary>().ToArray();

XML Serializing information on Base class and inherited class

I am trying to serialize a C# object into XML so that it could be used as the body of API call. They are very particular about the input they need. I have built the following class to hold the data I need to send over to them. Including attributes and all properties as Elements instead of attributes. They also require that lists include the type="array" I though that creating my own class the implements a List would be the easiest since all lists I give them must have the same attribute. When serialization occurs it serializes the base class of List items but it doesn't include the attribute I want from the derived class.
public class CustomArray<T> : List<T>
{
[XmlAttribute]
public string type { get; set; } = "array";
}
[XmlRoot("message")]
public class MessageBody
{
[XmlArray("Checks"), XmlArrayItem("CheckItem")]
public CustomArray<Check> CheckList { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
[XmlArray("InvoiceList"), XmlArrayItem("Invoice")]
public CustomArray<Invoice> InvoiceList { get; set; }
}
public class Invoice
{
[XmlElement("C_INVOICE_ID")]
public long ID { get; set; }
[XmlElement("C_INVOICE_NUM")]
public string InvoiceNum { get; set; }
}
I then run this code:
// Create a sample object
var message = new MessageBody()
{
CheckList = new CustomArray<Check>
{
new Check
{
CheckNumber = "111",
Amount = 1.00M
},
new Check
{
CheckNumber = "112",
Amount = 2.00M,
InvoiceList = new CustomArray<Invoice>
{
new Invoice
{
ID = 1,
InvoiceNum = "1"
}
}
}
}
};
// Create custom settings
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
// Serialize item and print it to console
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, settings))
{
var serializer = new XmlSerializer(message.GetType());
serializer.Serialize(writer, message, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
Console.WriteLine(sw.ToString());
}
I get this written to the console:
<message>
<Checks>
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList>
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
But I need to get this:
<message>
<Checks type="array">
<CheckItem>
<C_CHECK_NUMBER>111</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>1.00</C_CHECK_AMOUNT>
</CheckItem>
<CheckItem>
<C_CHECK_NUMBER>112</C_CHECK_NUMBER>
<C_CHECK_AMOUNT>2.00</C_CHECK_AMOUNT>
<InvoiceList type="array">
<Invoice>
<C_INVOICE_ID>1</C_INVOICE_ID>
<C_INVOICE_NUM>1</C_INVOICE_NUM>
</Invoice>
</InvoiceList>
</CheckItem>
</Checks>
</message>
Thank you for your help!
Here is a dotnetfiddle that I made to show it off. It's not exact but it has the same idea. https://dotnetfiddle.net/ALCX5H
Try following :
[XmlRoot("message")]
public class MessageBody
{
[XmlElement("Checks")]
public Checks Checks { get; set; }
}
public class Checks
{
[XmlAttribute]
public string type { get; set; }
[XmlElement("Checks")]
public List<Check> Checks { get; set; }
}
public class Check
{
[XmlElement("C_CHECK_NUMBER")]
public string CheckNumber { get; set; }
[XmlElement("C_CHECK_AMOUNT")]
public decimal Amount { get; set; }
}

C# SelectNodes for main node attribute

I'm trying to obtain the value of the logicalName attribute of the main node of this xml file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<ticketlayout xmlns="http://www.example.com/ticketlayout" logicalName="target.xml" deviceCode="1" measurement="mm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/ticketlayout">
<fontdefinition id="BarCode">
<fontname>Code128bWin</fontname>
<size measure="pt">16</size>
</fontdefinition>
</ticketlayout>
I've tried to add the namespace "xsi", "http://www.w3.org/2001/XMLSchema-instance" this way:
XmlDocument fLayout = new XmlDocument();
fLayout.Load("myFile.xml");
XmlNamespaceManager nsmRequestLayout = new XmlNamespaceManager(fLayout.NameTable);
nsmRequestLayout.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
string sValue = fLayout.SelectNodes("//ticketlayout", nsmRequestLayout)[0].Attributes["name"].Value;
But I get no nodes. I've tried without namespace and no nodes again, and son on.
¿Could please anyone help me?
Thanks in advance.
If you want to get the value : target.xml
Try this code
XmlDocument fLayout = new XmlDocument();
fLayout.Load("myFile.xml"); // your XML file
var attrib = fLayout["ticketlayout"].Attributes["logicalName"].Value;
First of all your XML is not valid.
I modified to look this way in order to achieve what you are looking for.
XML File :
<?xml version="1.0" encoding="UTF-8"?>
<ticketlayout xmlns="http://www.example.com/ticketlayout" logicalName="target.xml" deviceCode="1" measurement="mm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com/ticketlayout">
<fontdefinition id="BarCode">
<fontname>Code128bWin</fontname>
<size measure="pt">16</size>
</fontdefinition>
</ticketlayout>
I am not sure why you would not have a model structure to deserialize you xml, and then access whatever property/attribute you need.
Example :
Classes:
[XmlRoot(ElementName = "size", Namespace = "http://www.example.com/ticketlayout")]
public class Size
{
[XmlAttribute(AttributeName = "measure")]
public string Measure { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "fontdefinition", Namespace = "http://www.example.com/ticketlayout")]
public class Fontdefinition
{
[XmlElement(ElementName = "fontname", Namespace = "http://www.example.com/ticketlayout")]
public string Fontname { get; set; }
[XmlElement(ElementName = "size", Namespace = "http://www.example.com/ticketlayout")]
public Size Size { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "ticketlayout", Namespace = "http://www.example.com/ticketlayout")]
public class Ticketlayout
{
[XmlElement(ElementName = "fontdefinition", Namespace = "http://www.example.com/ticketlayout")]
public Fontdefinition Fontdefinition { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName = "logicalName")]
public string LogicalName { get; set; }
[XmlAttribute(AttributeName = "deviceCode")]
public string DeviceCode { get; set; }
[XmlAttribute(AttributeName = "measurement")]
public string Measurement { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string SchemaLocation { get; set; }
}
Then you could use a serializer :
public class Serializer
{
public T Deserialize<T>(string input) where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader stringReader = new StringReader(input))
{
return (T)xmlSerializer.Deserialize(stringReader);
}
}
public string Serialize<T>(T ObjectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());
StringBuilder builder = new StringBuilder();
using (StringWriterWithEncoding textWriter = new StringWriterWithEncoding(builder, Encoding.UTF8))
{
xmlSerializer.Serialize(textWriter, ObjectToSerialize);
return textWriter.ToString();
}
}
}
public class StringWriterWithEncoding : StringWriter
{
Encoding encoding;
public StringWriterWithEncoding(StringBuilder builder, Encoding encoding)
: base(builder)
{
this.encoding = encoding;
}
public override Encoding Encoding
{
get { return encoding; }
}
}
And finally you can access whatever you want by doing the following :
var serializer = new Serializer();
//I used a local file for testing, but it should be the same thing with your file
var xmlInputData = File.ReadAllText(#"MyXmlPath");
var output = serializer.Deserialize<Ticketlayout >(xmlInputData);
var logicalName = output.LogicalName;

DataContractSerializer - Namespace issues

I'm using HttpClient to post xml to a rest service. The problem is the service expects namespace prefix's in fashion that I'm unable to achieve with DataContractSerializer.
Expected xml:
<gto:createRequest xmlns:gto="http://www...com/sign">
<userId></userId>
<visibleDataContentType></visibleDataContentType>
<visibleData></visibleData>
<hiddenData></hiddenData>
<expiryInSeconds></expiryInSeconds>
</gto:createRequest>
The object i'm serialzing:
namespace ABC
{
[DataContract(Name = "createRequest", Namespace = "http://www...com/sign")]
public class CreateRequest
{
[DataMember(Name = "userId")]
public string UserId { get; set; }
[DataMember(Name = "visibleDataContentType")]
public string VisibleDataContentType { get; set; }
[DataMember(Name = "visibleData")]
public string VisibleData { get; set; }
[DataMember(Name = "hiddenData")]
public string HiddenData { get; set; }
[DataMember(Name = "expiryInSeconds")]
public int ExpiryInSeconds { get; set; }
}
}
I can't get the prefix "gto: createRequest", this what DataContractSerializer outputs:
<createRequest xmlns="http://www...com/sign" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<expiryInSeconds></expiryInSeconds>
<hiddenData></hiddenData>
<userId></userId>
<visibleData></visibleData>
<visibleDataContentType></visibleDataContentType>
</createRequest>
I have tried the old XmlSerializer but with no luck. Any ideas!?
Update: The namespace prefix does not have to be gto: but i has to be there!
Update: the output from Ondrej Svejdars answer that the server doesn't accept:
<gto:createRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:gto="http://www.test.com/sign">
<gto:expiryInSeconds>60</gto:expiryInSeconds>
<gto:hiddenData>hidden</gto:hiddenData>
<gto:userId>123456</gto:userId>
<gto:visibleData>visible</gto:visibleData>
<gto:visibleDataContentType>text/plain</gto:visibleDataContentType>
</gto:createRequest>
[Edited to match the gto: only on top element]
You can tweak xml writer:
public class XmlProxyWritter : XmlTextWriter {
private string m_NS;
public XmlProxyWritter(string ns, TextWriter w)
: base(w) {
m_NS = ns;
}
public XmlProxyWritter(string ns, Stream w, Encoding encoding)
: base(w, encoding) {
m_NS = ns;
}
public XmlProxyWritter(string ns, string filename, Encoding encoding)
: base(filename, encoding) {
m_NS = ns;
}
public override string LookupPrefix(string ns) {
if (string.Compare(ns, m_NS, StringComparison.OrdinalIgnoreCase) == 0) {
return "gto";
}
return base.LookupPrefix(ns);
}
public override void WriteStartElement(string prefix, string localName, string ns) {
if (string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(ns)) {
prefix = LookupPrefix(ns);
}
base.WriteStartElement(prefix, localName, ns);
}
}
Business class:
[XmlRoot(ElementName = "createRequest", Namespace = "http://www.test.com/sign")]
public class CreateRequest {
[XmlElement(ElementName="userId", Namespace = "")]
public string UserId { get; set; }
[XmlElement(ElementName = "visibleDataContentType", Namespace = "")]
public string VisibleDataContentType { get; set; }
[XmlElement(ElementName = "visibleData", Namespace = "")]
public string VisibleData { get; set; }
[XmlElement(ElementName = "hiddenData", Namespace = "")]
public string HiddenData { get; set; }
[XmlElement(ElementName = "expiryInSeconds", Namespace = "")]
public int ExpiryInSeconds { get; set; }
}
Call example (where http://www.test.com/sign is the namespace of CreateRequest)
string result;
var serXml = new XmlSerializer(typeof(CreateRequest));
using (var stream = new MemoryStream()) {
using (var writer = new XmlProxyWritter("http://www.test.com/sign", stream, Encoding.UTF8)) {
serXml.Serialize(writer, new CreateRequest {
ExpiryInSeconds = 1,
HiddenData = "my preasures",
UserId = "Pepa"
});
}
result = Encoding.UTF8.GetString(stream.ToArray());
}
Output:
<?xml version="1.0" encoding="utf-8"?>
<gto:createRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:gto="http://www.test.com/sign">
<userId>Pepa</userId>
<hiddenData>my preasures</hiddenData>
<expiryInSeconds>1</expiryInSeconds>
</gto:createRequest>
Which works for you (I hope) but it feels like kind of hacking; maybe the proper solution here would be to "teach" server the correct xml format ? :)

C# XML Deserialize Array elements null

I'm trying to deserialize a reponse from a REST API.
"<FieldListDTO xmlns=\"api.playcento.com/1.0\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<Allfield>
<FieldDTO>
<Fieldname>Mobile nr</Fieldname>
<Fieldtype>T</Fieldtype>
<Fieldvalue>003241234578</Fieldvalue>
<Fk_id_page>CP584ea74ce5ad4e2d8561d75fc6944f96</Fk_id_page>
<Id_field>FI152dcde5ef9849898b12d6a3f2cdb4ee</Id_field>
<Required>true</Required>
</FieldDTO>
</Allfield>
<Totalcount>1</Totalcount>
</FieldListDTO>"
The Field class:
namespace PlaycentoAPI.Model
{
[XmlRoot("FieldListDTO",Namespace = "api.playcento.com/1.0")]
[XmlType("FieldListDTO")]
public class FieldListDTO
{
public FieldListDTO() { }
[XmlElement("Totalcount")]
public int TotalCount { get; set; }
[XmlArray("Allfield")]
[XmlArrayItem("FieldDTO", typeof(Field))]
public Field[] Field { get; set; }
}
[XmlRoot("FieldDTO", Namespace = "api.paycento.com/1.0")]
[XmlType("FieldDTO")]
public class Field
{
public Field()
{
}
[XmlElement("Id_field")]
public string ID_Field { get; set; }
[XmlElement("Fieldtype")]
public string FieldType { get; set; }
[XmlElement("Fk_id_page")]
public string FK_ID_PAGE { get; set; }
[XmlElement("Required")]
public bool Required { get; set; }
[XmlElement("Fieldname")]
public string FieldName { get; set; }
[XmlElement("Fieldvalue")]
public string FieldValue { get; set; }
}
}
My code which calls the API and deserializes it:
string response = Helper.PerformAndReadHttpRequest(uri, "GET", "");
FieldListDTO myObject;
XmlReaderSettings settings = new XmlReaderSettings();
using (StringReader textReader = new StringReader(response))
{
using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
{
XmlSerializer mySerializer = new XmlSerializer(typeof(FieldListDTO));
myObject = (FieldListDTO)mySerializer.Deserialize(xmlReader);
}
}
return myObject.Field;
In my actual response, I'm getting back 14 FieldDTO's. After deserializing the xml, FieldListDTO myObject contains TotalCount = 14 and Field is an array containing 14 Field's. But all the properties of these fields are NULL (or false).
I'm using the same method for several other API calls. I've compared the classes and the only difference that I see is that the class (Field) has an bool property. So I thought that was the problem. I've changed the bool property to a string but still all the properties were NULL after deserialization.
First thing to catch my eye is that the namespace in your FieldDTO class doesn't match the one in the XML document.
"api.paycento.com/1.0"
"api.playcento.com/1.0"

Categories