XmlAttribute/XmlText cannot be used to encode complex type - c#

I want to serialize a class Ticket into xml. I get the error :"XmlAttribute/XmlText cannot be used to encode complex type" because of my customfield class.
This is how the xml for customfields should look like ( the attribute array is nesseray but I don't understand how to create it):
<custom_fields type="array">
<custom_field name="Standby Reason" id="6">
<value/>
</custom_field>
<custom_field name="Close Date" id="84">
Class Ticket
public class Ticket
{
[XmlElement("custom_fields")]
public CustomFields Custom_fields { get; set; }
Class CustomFields
[Serializable]
public class CustomFields
{
[XmlAttribute("array")]
public List<CustomField> custom_field { get; set; }
Class CustomField
[Serializable]
public class CustomField
{
[XmlIgnore]
public string Name { get; set; }
[XmlElement]
public int Id { get; set; }
[XmlElement]
public string Value { get; set; }
Serialize Method
public string Serialize(object obj)
{
var nsSerializer = new XmlSerializerNamespaces();
nsSerializer.Add(String.Empty, String.Empty);
var serializer = new XmlSerializer(typeof(Ticket), String.Empty);
using (StringWriter writer = new StringWriter())
{
ExtendedXmlTextWriter xmlTextWriter = new ExtendedXmlTextWriter(writer);
serializer.Serialize(xmlTextWriter, obj, nsSerializer);
//return writer.ToString();
XElement root = new XElement("custom_fields", new XAttribute("type", "array"),
new XElement("custom_field",
new XAttribute("name", "Standby Reason"),
new XAttribute("id", 6)
), new XElement("value"),
new XElement("custom_field",
new XAttribute("name", "Close Date"),
new XAttribute("id", 84)
)
);
return (writer.ToString() + root.ToString());
}

Sometimes Linq To Xml can be very helpful
XElement root = new XElement("ticket",
new XElement("custom_fields",
new XAttribute("type", "array"),
new XElement("custom_field",
new XAttribute("name", "Standby Reason"),
new XAttribute("id", 6)
),
new XElement("value"),
new XElement("custom_field",
new XAttribute("name", "Close Date"),
new XAttribute("id", 84)
)
)
);
string xml = root.ToString();
OUTPUT:
<ticket>
<custom_fields type="array">
<custom_field name="Standby Reason" id="6" />
<value />
<custom_field name="Close Date" id="84" />
</custom_fields>
</ticket>

Class Ticket
public class Ticket
{
[XmlElement("custom_fields")]
public CustomFields Custom_fields { get; set; }
Class CustomFields
[Serializable]
public class CustomFields
{
[XmlArray("array"), XmlArrayItem("custom_field")]
public List<CustomField> custom_field { get; set; }
Class CustomField
[Serializable]
public class CustomField
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement("value")]
public string Value { get; set; }
XML:
<ticket>
<custom_fields>
<array>
<custom_field name="Standby Reason" id="6"><value /></custom_field>
<custom_field name="Close Date" id="84"><value /></custom_field>
</array>
</custom_fields>
</ticket>

List<CustomField> can not be XML attribute.

Related

XmlSerializer not correct format result

The output i want as below
<SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body >
</SOAP:Envelope >
The output xml that i am getting as my result
<?xml version="1.0"?>
<SOAP:Envelope xmlns:m="http://www.e-courier.com/schemas/" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body UserGUID="">
<m:SaveOrder >
<m:Order UserID="" Notes="" CustomerID="" />
</m:SaveOrder>
</SOAP:Body>
</SOAP:Envelope>
My XML Class code:
[XmlRoot(ElementName="Order")]
public class Order {
[XmlAttribute(AttributeName="UserID")]
public string UserID { get; set; }
[XmlAttribute(AttributeName="Notes")]
public string Notes { get; set; }
[XmlAttribute(AttributeName="CustomerID")]
public string CustomerID { get; set; }
}
[XmlRoot(ElementName="SaveOrder", Namespace="http://www.e-courier.com/schemas/")]
public class SaveOrder {
[XmlElement(ElementName="Order")]
public Order Order { get; set; }
[XmlAttribute(AttributeName="m", Namespace="http://www.w3.org/2000/xmlns/")]
public string M { get; set; }
}
[XmlRoot(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Body {
[XmlElement(ElementName="SaveOrder", Namespace="http://www.e-courier.com/schemas/")]
public SaveOrder SaveOrder { get; set; }
[XmlAttribute(AttributeName="UserGUID")]
public string UserGUID { get; set; }
}
[XmlRoot(ElementName="Envelope", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope {
[XmlElement(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName="SOAP", Namespace="http://www.w3.org/2000/xmlns/")]
public string SOAP { get; set; }
}
My Code where i am generating xml
var SaveOrder = new ECSaveOrderRequest.Envelope
{
Body = new ECSaveOrderRequest.Body
{
UserGUID = guid,
SaveOrder = new ECSaveOrderRequest.SaveOrder
{
Order = new ECSaveOrderRequest.Order
{
UserID = Uid,
Notes = "",
CustomerID=""
}
}
}
};
var ns = new XmlSerializerNamespaces();
ns.Add("SOAP", "http://schemas.xmlsoap.org/soap/envelope/");
ns.Add("m", "http://www.e-courier.com/schemas/");
var ser = new XmlSerializer(typeof(ECSaveOrderRequest.Envelope));
using (var ms = new MemoryStream())
{
// write the DTO to the MemoryStream
ser.Serialize(ms, SaveOrder, ns);
using (var wc = new WebClient())
{
wc.Encoding = System.Text.Encoding.UTF8;
ms.Position = 0;
StreamReader stream = new StreamReader(ms);
string requestString = stream.ReadToEnd();
var resp = wc.UploadData(ECUrl, ms.ToArray());
}
}
You need to explicitly clear the xml namespace on SaveOrder.Order or the serializer will default to SaveOrder's xml namespace.
Here you go:
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace ECSaveOrderRequest
{
/*
* <SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body >
</SOAP:Envelope >*/
public class Order
{
[XmlAttribute(AttributeName = "UserID")]
public string UserID { get; set; }
[XmlAttribute(AttributeName = "Notes")]
public string Notes { get; set; }
[XmlAttribute(AttributeName = "CustomerID")]
public string CustomerID { get; set; }
}
public class SaveOrder
{
[XmlElement(ElementName = "Order", Namespace = "")]
public Order Order { get; set; }
}
public class Body
{
[XmlElement(ElementName = "SaveOrder", Namespace = "http://www.e-courier.com/schemas/")]
public SaveOrder SaveOrder { get; set; }
[XmlAttribute(AttributeName = "UserGUID")]
public string UserGUID { get; set; }
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName = "SOAP", Namespace = "http://www.w3.org/2000/xmlns/")]
public string SOAP { get; set; }
}
class Program
{
static void Main(string[] args)
{
var SaveOrder = new ECSaveOrderRequest.Envelope
{
Body = new ECSaveOrderRequest.Body
{
UserGUID = "{redacted}",
SaveOrder = new ECSaveOrderRequest.SaveOrder
{
Order = new ECSaveOrderRequest.Order
{
UserID = "1",
Notes = "Signature Requiered",
CustomerID = "3"
}
}
}
};
var ns = new XmlSerializerNamespaces();
ns.Add("SOAP", "http://schemas.xmlsoap.org/soap/envelope/");
ns.Add("m", "http://www.e-courier.com/schemas/");
var ser = new XmlSerializer(typeof(ECSaveOrderRequest.Envelope));
var ms = new MemoryStream();
// write the DTO to the MemoryStream
ser.Serialize(ms, SaveOrder, ns);
ms.Position = 0;
var xml = Encoding.UTF8.GetString(ms.GetBuffer());
Console.WriteLine(xml);
Console.ReadKey();
}
}
}
outputs
<?xml version="1.0"?>
<SOAP:Envelope xmlns:m="http://www.e-courier.com/schemas/" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body UserGUID="{redacted}">
<m:SaveOrder>
<Order UserID="1" Notes="Signature Requiered" CustomerID="3" />
</m:SaveOrder>
</SOAP:Body>
</SOAP:Envelope>
Which is a serialization of the same XML document as
<SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body>
</SOAP:Envelope>
.

Multiple, identical elements in the same C# DTO

I am trying to create a WCF-service, which can return me an XML document looking something like this:
<alert>
<identifier>SecretID</identifier>
<info>
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info>
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</alert>
The DTO of this file would look something like this:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public List<Info> Info { get; set; }
}
[DataContract(Name = "info", Namespace = "")]
public class Info
{
[DataMember(Name = "valueName")]
public string ValueName { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}
However, when I try with the following:
var alert = new Alert()
{
Identifier = "SecretID",
Info = new List<Info>
{
new Info() {ValueName = "Name1", Value = "Info1"},
new Info() {ValueName = "Name2", Value = "Info2"},
}
}
I get:
<alert>
<identifier>SecretID</identifier>
<info>
<info xmlns="">
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info xmlns="">
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</info>
</alert>
I don't need the extra <info> tag, and the namespace xmlns="" would be nice to have removed too. What should I do to get rid of it?
You can use dictionary or key value pairs as mentioned below:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public KeyValuePair<string, string> Info { get; set; }
}
var alert = new Alert()
{
Identifier = "SecretID",
Info = new KeyValuePair<string, string>()
}

How to create an XML file from this c# class?

I want to create an XML file from the following c# class and vice-verse. How can i do this?
public class Settings
{
public string Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public string ParentId { get; set; }
public List<Settings> SubSettings { get; set; }
public bool IsRoot
{
get
{
return string.IsNullOrEmpty(ParentId);
}
}
}
You can serialize a class in C# using XmlSerializer like this:
var s = new Settings()
{
Id = "id",
Name = "name",
ParentId = "parentId",
Value = "value",
SubSettings = new List<Settings>()
{
new Settings()
{
Id = "subId",
Name = "subName",
ParentId = "subParentId",
Value = "subValue",
SubSettings = new List<Settings>()
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
string fileName = "C:\\test.xml";
using (FileStream fs = File.Open(fileName, FileMode.CreateNew))
{
serializer.Serialize(fs, s);
}
This is the result that I get:
<?xml version="1.0"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>id</Id>
<Name>name</Name>
<Value>value</Value>
<ParentId>parentId</ParentId>
<SubSettings>
<Settings>
<Id>subId</Id>
<Name>subName</Name>
<Value>subValue</Value>
<ParentId>subParentId</ParentId>
<SubSettings />
</Settings>
</SubSettings>
</Settings>
You can then deserialize it back to an object like this:
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
Stream fs = new FileStream("C:\\test.xml", FileMode.Open);
Settings settings = (Settings)serializer.Deserialize(fs);

XML Deserialization how to

i have the following XML file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<a>
<b attr0="">
<c>
<d attr1="" attr2="">
<e>
<f/>
<g/>
<h/>
<i/>
</e>
</d>
<!-- ...more d's -->
</c>
</b>
<b>
<c>
<d attr1="" attr2="">
<e>
<f/>
<g/>
<h/>
<i/>
</e>
</d>
<!-- ...more d's -->
</c>
</b>
<!-- ...more b's -->
</a>
I want to deserialize it into C# objects, i am using the following classes:
Class a:
[XmlRoot(ElementName = "a")]
public class a
{
[XmlElement("b")]
List<b> bs = new List<b>();
}
Class b:
public class b
{
[XmlAttribute("attr0")]
String attr0{ get; set; }
[XmlElement("c")]
c c1 = new c();
}
Class c:
public class c
{
[XmlElement("d")]
List<d> ds = new List<d>();
}
Class d:
public class d
{
[XmlAttribute(AttributeName = "attr1")]
String attr1{ get; set; }
[XmlAttribute(AttributeName = "attr2")]
String attr2{ get; set; }
[XmlElement("e")]
List<e> es = new List<e>();
}
and Class e:
public class e
{
[XmlText]
String f { get; set; }
[XmlText]
String g { get; set; }
[XmlText]
String h { get; set; }
[XmlText]
String i { get; set; }
}
And with the following code i try to deserialize it:
public a deserialize()
{
XmlSerializer deserializer = new XmlSerializer(typeof(a));
System.IO.TextReader reader = new System.IO.StreamReader(#"C:\file.xml");
object obj = deserializer.Deserialize(reader);
a XmlData = (a)obj;
reader.Close();
return a;
}
Well right now, nothing is working.
I was trying to add a XMLArray tag on it, but didn't work.
You guys would do me a big favor for some good advice :)
There are two issues in your code.
1) Declare your class members' access modifiers that you want to de/serialize to public like this. The default serialization implementation only works with public members.
[XmlRoot(ElementName = "a")]
public class a
{
[XmlElement("b")]
public List<b> bs = new List<b>();
}
2) You can't declare multiple [XmlText] in a single object, in your case class e. Change them to [XmlElement], instead.
public class e
{
[XmlElement]
public String f { get; set; }
[XmlElement]
public String g { get; set; }
[XmlElement]
public String h { get; set; }
[XmlElement]
public String i { get; set; }
}
Then it will work.
XElement xmlFile= new XElement("a",
new XElement("b",
new XElement("c",
new XElement("d", new XAttribute("attr1"," ") , new XAttribute("attr2"," ")),
new XElement("e", new XElement("f"), new XElement("g"), new XElement("h"), new XElement("i")),
new XElement("c",
new XElement("d", new XAttribute("attr1"," ") , new XAttribute("attr2"," ")),
new XElement("e", new XElement("f"), new XElement("g"), new XElement("h"), new XElement("i")),
new XElement("Department", new XAttribute("Name","Automobile"))
),
new XElement("b",
new XElement("c",
new XElement("d", new XAttribute("attr1"," ") , new XAttribute("attr2"," ")),
new XElement("e", new XElement("f"), new XElement("g"), new XElement("h"), new XElement("i")),
new XElement("c",
new XElement("d", new XAttribute("attr1"," ") , new XAttribute("attr2"," ")),
new XElement("e", new XElement("f"), new XElement("g"), new XElement("h"), new XElement("i"))
)
);
xmlFile.Save(#"D:\file.xml");
try this if it helps resolving your problem. And you can deserialize in the same manner i have serialized.
EDIT:
try to add the last line in your code too. I tried adding in the part of code section but it's not happening somehow.

Serialize only those properties which are specified [XmlElement] without changing the original class

Code:
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
}
Now I want to serilize only thoese which are specified with [XmlElement], companyid not to be serilized.
So, what can I do?
Here's a simple example I put together in LinqPad. The first 4 lines of the Main method set up an XmlAttributeOverrides instance that is then used to tell the XmlSerializer to not serialize the companyid property.
void Main()
{
//Serialize, but ignore companyid
var overrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes();
attributes.XmlIgnore = true;
overrides.Add(typeof(MyClass), "companyid", attributes);
using(var sw = new StringWriter()) {
var xs = new XmlSerializer(typeof(MyClass), overrides);
var a = new MyClass() {
Company = "Company Name",
Amount = 10M,
companyid = 7
};
xs.Serialize(sw, a);
Console.WriteLine(sw.ToString());
}
}
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
}
The output of this program is:
<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Company>Company Name</Company>
<Amount>10</Amount>
</MyClass>
If you need this code to inspect the class to determine which properties to exclude based on an XmlElementAttribute not being present, then you can modify the above code to use reflection to enumerate the properties of the object. For every property that does not have an XmlElementAttribute, add an item to the overrides instance.
For example:
void Main()
{
//Serialize, but ignore properties that do not have XmlElementAttribute
var overrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes();
attributes.XmlIgnore = true;
foreach(var prop in typeof(MyClass).GetProperties())
{
var attrs = prop.GetCustomAttributes(typeof(XmlElementAttribute));
if(attrs.Count() == 0)
overrides.Add(prop.DeclaringType, prop.Name, attributes);
}
using(var sw = new StringWriter()) {
var xs = new XmlSerializer(typeof(MyClass), overrides);
var a = new MyClass() {
Company = "Company Name",
Amount = 10M,
companyid = 7,
blah = "123" };
xs.Serialize(sw, a);
Console.WriteLine(sw.ToString());
}
}
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
public string blah { get; set; }
}

Categories

Resources