Adding a String Indexer to XmlSerializer - c#

I have an XML source that I can't change and I want to deserialise this using XmlSerializer.
I can do this fine however there are some arrays of custom classes that I would like to access the array by a String and not in Integer.
I know I can use
public ClassName this[string index]
but I can't work out where to add this to my Class.
I want to be able to call
Object.Transaction["TransactionTypeName"]
instead of
Object.Transaction[0]
This is a stripped down version of the class.
public partial class Configuration
{
private ConfigurationTransaction[] transactionsField;
[System.Xml.Serialization.XmlArrayItemAttribute("Transaction", IsNullable = false)]
public List<ConfigurationTransaction> Transactions
{
get
{
return this.transactionsField;
}
set
{
this.transactionsField = value;
}
}
}
public partial class ConfigurationTransaction
{
private string typeField;
[System.Xml.Serialization.XmlAttributeAttribute()]
public string type
{
get
{
return this.typeField;
}
set
{
this.typeField = value;
}
}
}

A simplified example code of your classes with indexer.
While we need to save the value to access the indexer will have to create additional class.
public class Configuration
{
public ConfigurationTransaction this[string transactionName]
{
get
{
return Transactions.First(tran => tran.TransactionName == transactionName).ConfigurationTransaction;
}
set
{
int index = Transactions.FindIndex(tran => tran.TransactionName == transactionName);
if (index >= 0)
Transactions[index] = new PairHelper { TransactionName = transactionName, ConfigurationTransaction = value };
else
Transactions.Add(new PairHelper { TransactionName = transactionName, ConfigurationTransaction = value });
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public List<PairHelper> Transactions { get; set; }
}
public class ConfigurationTransaction
{
[XmlAttribute()]
public string Type { get; set; }
}
public class PairHelper
{
public string TransactionName { get; set; }
public ConfigurationTransaction ConfigurationTransaction { get; set; }
}
It's work:
Configuration conf = new Configuration();
conf.Transactions = new List<PairHelper>();
conf["fooTran"] = new ConfigurationTransaction { Type = "foo" };
conf["barTran"] = new ConfigurationTransaction { Type = "bar" };
var xs = new XmlSerializer(typeof(Configuration));
using (var fs = new FileStream("test.txt", FileMode.Create))
{
xs.Serialize(fs, conf);
}
Configuration conf2;
using (var fs = new FileStream("test.txt", FileMode.Open))
{
conf2 = (Configuration)xs.Deserialize(fs);
}
foreach (var tran in conf2.Transactions)
Console.WriteLine(tran.TransactionName + " : " + tran.ConfigurationTransaction);
Console.WriteLine(conf2["fooTran"].Type);
Console.WriteLine(conf2["barTran"].Type);
XML will look like this:
<?xml version="1.0"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Transactions>
<PairHelper>
<TransactionName>fooTran</TransactionName>
<ConfigurationTransaction Type="foo" />
</PairHelper>
<PairHelper>
<TransactionName>barTran</TransactionName>
<ConfigurationTransaction Type="bar" />
</PairHelper>
</Transactions>
</Configuration>

I was able to add in this into the Configuration class.
public ConfigurationTransaction this[String index]
{
get
{
foreach (var item in Transactions)
{
if (item.type.ToLower().Trim() == index.ToLower().Trim())
{
return item;
}
}
return null;
}
}
And call it like
Object["TransactionTypeName"]
I think that's what I did anyway, I ended up manually importing all the data into a SQL database and creating an Entity Framework class to access it and depricated the XML solution.

Related

Protobuf-net Serializing Parent Class inherited object property marked as [ProtoIgnore()] throwing "No serializer defined for type: System.Object"

All the objects in our system inherit a base class which has got a property of type object.
I have tried adding protoignore attribute to all the properties of the base class as well but that doesn't seem to work as well.
class Program
{
static void Main(string[] args)
{
Vehicle vehicle = new Vehicle();
vehicle.BodyStyleDescription = "4x4";
vehicle.BodyStyleText = "Prestige Medium";
dynamic protobufModel = TypeModel.Create();
AddTypeToModel<Vehicle>(protobufModel);
using (MemoryStream compressed = new MemoryStream())
{
using (GZipStream gzip = new GZipStream(compressed, CompressionMode.Compress, true))
{
protobufModel.Serialize(gzip, vehicle);
}
string str = Convert.ToBase64String(compressed.GetBuffer(), 0, Convert.ToInt32(compressed.Length));
}
}
public static MetaType AddTypeToModel<T>(RuntimeTypeModel typeModel)
{
var properties = typeof(T).GetProperties().Select(p => p.Name).OrderBy(name => name);
return typeModel.Add(typeof(T), true).Add(properties.ToArray());
}
}
Following is the hierarchy of the object
public interface IObjectBaseClass
{
[ProtoIgnore()]
object Parent { get; set; }
[ProtoIgnore()]
bool IsSaved { get; set; }
[ProtoIgnore()]
string XmlAtLoad { get; set; }
}
public class ObjectBaseClass : IObjectBaseClass
{
public ObjectBaseClass()
{
}
[ProtoIgnore()]
internal object _Parent;
[ProtoIgnore()]
internal bool _IsSaved;
[ProtoIgnore()]
internal string _XmlAtLoad;
[ProtoIgnore()]
public bool IsSaved
{
get { return _IsSaved; }
set { _IsSaved = value; }
}
[ProtoIgnore()]
public object Parent
{
get { return _Parent; }
set { _Parent = value; }
}
[ProtoIgnore()]
public string XmlAtLoad
{
get { return _XmlAtLoad; }
set { _XmlAtLoad = value; }
}
}
public class Vehicle : ObjectBaseClass
{
private string _BodyStyleText;
private string _BodyStyleDescription;
public string BodyStyleDescription
{
get { return _BodyStyleDescription; }
set { _BodyStyleDescription = value; }
}
public string BodyStyleText
{
get { return _BodyStyleText; }
set { _BodyStyleText = value; }
}
}
Your problem is that when you do typeModel.Add(typeof(T), true).Add(properties.ToArray()) you are adding all properties of T to the runtime type model, including those marked with ProtoIgnore. You can see this by calling the debugging method protobufModel.GetSchema(typeof(Vehicle)) which returns:
message Object {
}
message Vehicle {
optional string BodyStyleDescription = 1;
optional string BodyStyleText = 2;
optional bool IsSaved = 3;
optional Object Parent = 4;
optional string XmlAtLoad = 5;
}
To avoid adding properties marked with [ProtoIgnore], you could do:
public static MetaType AddTypeToModel<T>(RuntimeTypeModel typeModel)
{
var properties = typeof(T)
.GetProperties()
.Where(p => !p.GetCustomAttributes<ProtoIgnoreAttribute>().Any())
.Select(p => p.Name)
.OrderBy(name => name);
return typeModel.Add(typeof(T), true).Add(properties.ToArray());
}
Alternatively, since you are manually annotating some of your models with protobuf attributes anyway, you could mark the derived types with [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)], e.g.:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Vehicle : ObjectBaseClass
{
private string _BodyStyleText;
private string _BodyStyleDescription;
public string BodyStyleDescription
{
get { return _BodyStyleDescription; }
set { _BodyStyleDescription = value; }
}
public string BodyStyleText
{
get { return _BodyStyleText; }
set { _BodyStyleText = value; }
}
}
Using either method, the schema for Vehicle becomes:
message Vehicle {
optional string BodyStyleDescription = 1;
optional string BodyStyleText = 2;
}
This is what you require.

Deserialize XML String into Class

I am trying to deserialize an XML String into my class which is derived from another class but I am having a problem in doing so, I am getting the following error:
{"The specified type is abstract: name='DeviceRequest', namespace='', at <DeviceRequest xmlns=''>."}
I assume i am missing some decorator attribute that will inform the serializer how to deserialize this xml into the class?
Here is my code:
//Usage
DeviceRequest dreq = DeviceRequest.ParseRequest(e.XML);
//Base Class
public abstract class IFSFRequestBase
{
private string m_ApplicationSender = "";
private int m_WorkStationID = 0;
private string m_RequestID = "";
[XmlAttribute()]
public abstract string RequestType { get; set; }
public abstract byte[] Message();
[XmlAttribute()]
public string ApplicationSender
{
get
{
return m_ApplicationSender;
}
set
{
m_ApplicationSender = value;
}
}
[XmlAttribute()]
public int WorkStationID
{
get
{
return m_WorkStationID;
}
set
{
m_WorkStationID = value;
}
}
[XmlAttribute()]
public string RequestID
{
get
{
return m_RequestID;
}
set
{
m_RequestID = value;
}
}
}
//Derived Class
public abstract class DeviceRequest : IFSFRequestBase
{
private string m_TerminalID = "";
private string m_SequenceID = "";
private Output m_OutputField = null;
[XmlAttribute(), DefaultValue("")]
public string TerminalID
{
get
{
return m_TerminalID;
}
set
{
m_TerminalID = value;
}
}
[XmlAttribute(), DefaultValue("")]
public string SequenceID
{
get
{
return m_SequenceID;
}
set
{
m_SequenceID = value;
}
}
[XmlElement("Output")]
public Output OutputField
{
get
{
return m_OutputField;
}
set
{
m_OutputField = value;
}
}
public static DeviceRequest ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(DeviceRequest));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (DeviceRequest)serializer.Deserialize(XMLWithoutNamespace);
}
}
// helper class to ignore namespaces when de-serializing
public class NamespaceIgnorantXmlTextReader : XmlTextReader
{
public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader) : base(reader) { }
public override string NamespaceURI
{
get { return ""; }
}
}
UPDATE:
OK based on the answers here. I have updated the code, I now have a class Display that derives from DeviceRequest. I now get the following error:
{"There is an error in XML document (2, 2)."}, {"<DeviceRequest xmlns=''> was not expected."}
public class Display : DeviceRequest
{
public static Display ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(Display));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (Display)serializer.Deserialize(XMLWithoutNamespace);
}
[XmlAttribute()]
public override string RequestType
{
get { return "Output"; } set { }
}
public override byte[] Message()
{
throw new NotImplementedException();
}
}
DeviceRequest dreq = Display.ParseRequest(e.XML);
As DeviceRequest is an abstract type, it cannot be instantiated directly. It is impossible to directly deserialize into instances of Device-Request. That said, if there are some non-abstract classes derived from it, please show some of them.
OK, I have resolved this issue now. Thanks for the input guys.
I needed to add:
[System.Xml.Serialization.XmlRootAttribute(ElementName = "DeviceRequest")]
to the Display Class
[System.Xml.Serialization.XmlRootAttribute(ElementName = "DeviceRequest")]
public class Display : DeviceRequest
{
public static Display ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(Display));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (Display)serializer.Deserialize(XMLWithoutNamespace);
}
[XmlAttribute()]
public override string RequestType
{
get { return "O"; } set { }
}
public override byte[] Message()
{
throw new NotImplementedException();
}
}

deserialize returns null objects c#

When I try to deserialize below XML I get error: Settings xmlns='' was not expected.
I have mapped the root node in my class below, why do I get that error?
my xml:
<Settings>
<Access>
<Phone hasTextField="true">
<Item description="CMS" />
</Phone>
</Access>
</Settings>
class
Settings.cs:
[XmlRoot("Settings")]
public class AccessNodes
{
[XmlElement("Access")]
public Access AccessList { get; set; }
}
[XmlType("Access")]
public class Access
{
[XmlElement("Phone")]
public AccessItem Phone { get; set; }
}
public class AccessItem
{
public AccessItem()
{
Items = new List<Item>();
}
[XmlAttribute("hasTextField")]
public bool HasTextField { get; set; }
[XmlElement("Item")]
public List<Item> Items { get; set; }
}
[XmlType("Item")]
public class Item
{
[XmlAttribute("description")]
public string Description { get; set; }
}
SettingsReader.cs
public static class SettingsReader<T>
{
public static T Deserialize(string basePath)
{
string filename = basePath + ".xml";
T t = default(T);
XmlSerializer xs = new XmlSerializer(typeof(T));
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
t = (T)xs.Deserialize(fs);
}
return t;
}
Call to SettingsReader.cs
Access access = SettingsReader<Access>.Deserialize(Server.MapPath("~/App_Data/access"));
You're trying to deserialize the wrong element - you're trying to deserialize the Access type, whereas your top node is Settings, which is the name applied to the AccessNodes type.
Try this instead:
AccessNodes settings = SettingsReader<AccessNodes>.Deserialize(...);

How to serialize google sitemap in c# using XmlSerializer

I want to serialize like this
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
</urlset>
But wrong result generated.
My class is here
[Serializable]
[XmlRoot("urlset")]
public class GoogleSiteMap
{
public GoogleSiteMap() {
xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
xmlnsNews = "http://www.google.com/schemas/sitemap-news/0.9";
Urls = new List<gUrlBase>();
}
[XmlAttribute]
public string xmlns { get; set; }
[XmlAttribute("news",Namespace="xmlns")]
public string xmlnsNews { get; set; }
[XmlElement("url")]
public List<gUrlBase> Urls { get; set; }
}
Serializer is here
public static void GenerateGoogle(GoogleSiteMap smap,string filePath) {
XmlSerializer ser = new XmlSerializer(typeof(GoogleSiteMap));
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
ser.Serialize(fs, smap);
fs.Close();
}
}
Then result is here
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:d1p1="xmlns" d1p1:news="http://www.google.com/schemas/sitemap-news/0.9"/>
What's wrong on my class declaration?
Another QUESTION 2
How can i declare like this
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
<url>
<loc>http://www.example.org/business/article55.html</loc>
<news:news></news:news>
</url>
<url>
<loc>http://www.example.org/business/page1.html</loc>
<lastmod>2010-10-10</lastmod>
<changefreq>weekly</changefreq>
</url>
</urlset>
my declaration is here
[XmlRoot("urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
public class GoogleSiteMap
{
public GoogleSiteMap()
{
Urls = new List<gUrlBase>();
}
//[XmlElement("url")]
[XmlElement("url",Type = typeof(gNormalUrl))]
[XmlElement("url",Type = typeof(gNewsUrl))]
public List<gUrlBase> Urls { get; set; }
}
This is return error
The XML element 'url' from namespace 'http://www.sitemaps.org/schemas/sitemap/0.9' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.
How can i declare Same root name "url"?
You need to use the right namespace:
[XmlRoot("urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
you can take away xmlns and xmlnsNews properties - they do something else. Likewise, any data in ""http://www.google.com/schemas/sitemap-news/0.9" will need to be marked as such. Whether the namespace alias is news is irrelevant (it is only an alias), but that can be controlled via XmlSerializerNamespaces if you like. You do not need [Serializable].
For example, if each <url> needs to be in the "http://www.google.com/schemas/sitemap-news/0.9" namespace, and you want to use "http://www.sitemaps.org/schemas/sitemap/0.9" as the overall namespace and "http://www.google.com/schemas/sitemap-news/0.9" aliased as "news", then:
static class Program
{
static void Main()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "http://www.sitemaps.org/schemas/sitemap/0.9");
ns.Add("news", "http://www.google.com/schemas/sitemap-news/0.9");
var ser = new XmlSerializer(typeof (GoogleSiteMap));
var obj = new GoogleSiteMap {Urls = new List<string> {"abc", "def", "ghi"}};
ser.Serialize(Console.Out, obj, ns);
}
}
[XmlRoot("urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
public class GoogleSiteMap
{
[XmlElement("url", Namespace = "http://www.google.com/schemas/sitemap-news/0.9")]
public List<string> Urls { get; set; }
}
This generates:
<urlset
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<news:url>abc</news:url>
<news:url>def</news:url>
<news:url>ghi</news:url>
</urlset>
(I haven't checked what the actual content namespaces are - this is just to show the relationship between namespaces in the data, namespaces in the xml, and namespace-aliases)
Re your edit - something like:
static class Program
{
static void Main()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "http://www.sitemaps.org/schemas/sitemap/0.9");
ns.Add("news", "http://www.google.com/schemas/sitemap-news/0.9");
var ser = new XmlSerializer(typeof (GoogleSiteMap));
var obj = new GoogleSiteMap {Urls = {
new SiteUrl { Location = "http://www.example.org/business/article55.html", News = ""},
new SiteUrl { Location = "http://www.example.org/business/page1.html", LastModified = new DateTime(2010,10,10),
ChangeFrequency = "weekly"}
}};
ser.Serialize(Console.Out, obj, ns);
}
}
[XmlRoot("urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
public class GoogleSiteMap
{
private readonly List<SiteUrl> urls = new List<SiteUrl>();
[XmlElement("url")]
public List<SiteUrl> Urls { get { return urls; } }
}
public class SiteUrl
{
[XmlElement("loc")]
public string Location { get; set; }
[XmlElement("news", Namespace = "http://www.google.com/schemas/sitemap-news/0.9")]
public string News { get; set; }
[XmlElement("lastmod")]
public DateTime? LastModified { get; set; }
[XmlElement("changefreq")]
public string ChangeFrequency { get; set; }
public bool ShouldSerializeLastModified() { return LastModified.HasValue; }
}
which generates:
<urlset xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.org/business/article55.html</loc>
<news:news />
</url>
<url>
<loc>http://www.example.org/business/page1.html</loc>
<lastmod>2010-10-10T00:00:00</lastmod>
<changefreq>weekly</changefreq>
</url>
</urlset>
Please use below full code that made by me
Please see below full code
#region GoogleNewsSiteMap Class
[XmlRoot("urlset", Namespace = "http://www.sitemaps.org/schemas/sitemap/0.9")]
public class GoogleNewsSiteMap
{
const string _newsSiteMapSchema = "http://www.google.com/schemas/sitemap-news/0.9";
const string _newsSiteMapPrefix = "n";
public void Create(string loc, string prioity, string language, string name, string genres, string publicationDate, string title)
{
NewsSiteMap news = new NewsSiteMap();
news.Loc = loc;
news.Priority = prioity;
news.NewsSiteMapNews.Publication.Language = language;
news.NewsSiteMapNews.Publication.Name = name;
news.NewsSiteMapNews.Genres = genres;
news.NewsSiteMapNews.PublicationDate = publicationDate;
news.NewsSiteMapNews.Title = title;
List.Add(news);
}
public string GetXMLString()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = (" ");
settings.Encoding = new UTF8Encoding(false);
using (StringWriter str = new StringWriter())
using (XmlWriter writer = XmlWriter.Create(str, settings))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(_newsSiteMapPrefix, _newsSiteMapSchema);
XmlSerializer xser = new XmlSerializer(typeof(GoogleNewsSiteMap));
xser.Serialize(writer, this, ns);
return str.ToString();
}
}
private List<NewsSiteMap> _list = null;
[XmlElement("url")]
public List<NewsSiteMap> List
{
get
{
if (_list == null)
{
_list = new List<NewsSiteMap>();
}
return _list;
}
}
#region NewsSiteMap Class
public class NewsSiteMap
{
private string _loc = string.Empty;
private string _priority = string.Empty;
private NewsSiteMap_News _newsSiteMap_News = null;
[XmlElement("loc")]
public string Loc
{
get { return _loc; } set { _loc = value; }
}
[XmlElement("priority")]
public string Priority
{
get { return _priority; } set { _priority = value; }
}
[XmlElement("news", Namespace = _newsSiteMapSchema)]
public NewsSiteMap_News NewsSiteMapNews
{
get {
if (_newsSiteMap_News == null)
{
_newsSiteMap_News = new NewsSiteMap_News();
}
return _newsSiteMap_News;
}
set { _newsSiteMap_News = value; }
}
#region NewsSiteMap_News Class
public class NewsSiteMap_News
{
private NewsSiteMap_Publication _publication = null;
private string _genres = string.Empty;
private string _publicationDate = string.Empty;
private string _title = string.Empty;
private string _keywords = string.Empty;
private string _stockTickers = string.Empty;
[XmlElement("publication", Namespace = _newsSiteMapSchema)]
public NewsSiteMap_Publication Publication
{
get
{
if (_publication == null)
{
_publication = new NewsSiteMap_Publication();
}
return _publication;
}
set { _publication = value; }
}
[XmlElement("genres")]
public string Genres
{
get { return _genres; }
set { _genres = value; }
}
[XmlElement("publication_date")]
public string PublicationDate
{
get
{
try
{
return string.Format("{0:s}", Convert.ToDateTime(_publicationDate)) + string.Format("{0:zzz}", Convert.ToDateTime(_publicationDate));
}
catch (Exception ex)
{
return _publicationDate;
}
}
set { _publicationDate = value; }
}
public string Title
{
set { _title = value; }
}
[XmlElement("title")]
public XmlCDataSection CTitle
{
get
{
XmlDocument doc = new XmlDocument();
return doc.CreateCDataSection(_title);
}
set { _title = value.Value; }
}
[XmlElement("keywords")]
public string Keywords
{
get { return _keywords; } set { _keywords = value; }
}
[XmlElement("stock_tickers")]
public string StockTickers
{
get { return _stockTickers; } set { _stockTickers = value; }
}
#region NewsSiteMap_Publication Class
public class NewsSiteMap_Publication
{
private string _name = string.Empty;
private string _language = string.Empty;
[XmlElement("name")]
public string Name
{
get { return _name; }
set { _name = value; }
}
[XmlElement("language")]
public string Language
{
get { return _language; }
set { _language = value; }
}
}
#endregion NewsSiteMap_Publication Class
}
#endregion NewsSiteMap_News Class
}
#endregion NewsSiteMap Class
}
#endregion GoogleNewsSiteMap Class
Usage
Response.Clear();
Response.ContentType = "text/xml";
Response.ContentEncoding = System.Text.Encoding.UTF8;
GoogleNewsSiteMap googleNewsSiteMap = new GoogleNewsSiteMap();
googleNewsSiteMap.Create(/*put ur data*/);
Response.Write(googleNewsSiteMap.GetXMLString());

C# XML Serializer won't store an attribute

This is my first question on Stack Overflow. Apologies in advance if I don't do things quite right while I'm learning how things work here.
Here is my code :
public void TestSerialize()
{
ShoppingBag _shoppingBag = new ShoppingBag();
Fruits _fruits = new Fruits();
_fruits.testAttribute = "foo";
Fruit[] fruit = new Fruit[2];
fruit[0] = new Fruit("pineapple");
fruit[1]= new Fruit("kiwi");
_fruits.AddRange(fruit);
_shoppingBag.Items = _fruits;
Serialize<ShoppingBag>(_shoppingBag, #"C:\temp\shopping.xml");
}
public static void Serialize<T>(T objectToSerialize, string filePath) where T : class
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StreamWriter writer = new StreamWriter(filePath))
{
serializer.Serialize(writer, objectToSerialize);
}
}
[Serializable]
public class ShoppingBag
{
private Fruits _items;
public Fruits Items
{
get { return _items; }
set {_items = value; }
}
}
public class Fruits : List<Fruit>
{
public string testAttribute { get; set; }
}
[Serializable]
public class Fruit
{
public Fruit() { }
public Fruit(string value)
{
Name = value;
}
[XmlAttribute("name")]
public string Name { get; set; }
}
It produces this XML :
<?xml version="1.0" encoding="utf-8" ?>
<ShoppingBag xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<Fruit name="pineapple" />
<Fruit name="kiwi" />
</Items>
</ShoppingBag>
I don't understand why I am not getting <Items testAttribute="foo">
Please can anyone tell me what I need to add to my code so that the Serializer will write this attribute out?
Thanks,
You need an intermediary class there:
class Program
{
static void Main()
{
var shoppingBag = new ShoppingBag
{
Items = new ShoppingBagItems
{
Fruits = new List<Fruit>(new[] {
new Fruit { Name = "pineapple" },
new Fruit { Name = "kiwi" },
}),
TestAttribute = "foo"
}
};
var serializer = new XmlSerializer(typeof(ShoppingBag));
serializer.Serialize(Console.Out, shoppingBag);
}
}
public class ShoppingBag
{
public ShoppingBagItems Items { get; set; }
}
public class ShoppingBagItems
{
[XmlElement("Fruit")]
public List<Fruit> Fruits { get; set; }
[XmlAttribute("testAttribute")]
public string TestAttribute { get; set; }
}
public class Fruit
{
[XmlAttribute("name")]
public string Name { get; set; }
}
Also note that you don't need to decorate your classes with the [Serializable] attribute as it is used only for binary serialization. Another remark is that you don't need to derive from List<T>, simply use it as a property.
Unfortunately, when serializing a collection the XmlSerializer doesn't take into account the extra properties of that collection. It only considers the members that implement ICollection<T>. If you want to serialize extra attributes, you need to wrap the collection in another class that is not a collection itself.

Categories