public class MyStuff {
public string Name { get; set; }
public List<Annotation> Annotations { get; set; }
}
public class Annotation {
public string Name { get; set; }
public string Value { get; set; }
}
How do I get the List of Annotations to serialize as a bunch of XML attributes?
var x = new MyStuff {
Name = "Stuff",
Annotations = new [] {
new Annotation { Name = "Note1", Value = "blah" },
new Annotation { Name = "Note2", Value = "blahblah" }
}.ToList()
};
// turns into something like:
<MyStuff Name="Stuff" ann:Note1="blah" ann:Note2="blahblah" />
ann:Note1 is only valid if ann is an xml namespace,
XNamespace ns = "Annotation";
XElement xElem = new XElement("MyStuff", new XAttribute("Name",x.Name));
xElem.Add(x.Annotations
.Select(a => new XAttribute(ns + a.Name, a.Value)));
var xml = xElem.ToString();
OUTPUT:
<MyStuff Name="Stuff" p1:Note1="blah" p1:Note2="blahblah" xmlns:p1="Annotation" />
XmlDocument doc = new XmlDocument(); // Creating an xml document
XmlElement root =doc.CreateElement("rootelement"); doc.AppendChild(root); // Creating and appending the root element
XmlElement annotation = doc.CreateElement("Name");
XmlElement value = doc.CreateElement("Value");
annotation.InnerText = "Annotation Name Here";
value.InnerText = "Value Here";
doc.AppendChild(annotation);
doc.AppendChild(value);
You can narrow all your list and do the same thing in a loop.
What you can do is add the attribute [XmlAttribute] on your properties:
public class Annotation
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Value { get; set; }
}
And the result will be like this:
<Annotations>
<Annotation Name="Note1" Value="blah" />
<Annotation Name="Note2" Value="blahblah" />
</Annotations>
The IXmlSerializable interface allows you to customize the serialization of any class.
public class MyStuff : IXmlSerializable {
public string Name { get; set; }
public List<Annotation> Annotations { get; set; }
public XmlSchema GetSchema() {
return null;
}
public void ReadXml(XmlReader reader) {
// customized deserialization
// reader.GetAttribute() or whatever
}
public void WriteXml(XmlWriter writer) {
// customized serialization
// writer.WriteAttributeString() or whatever
}
}
Related
I am trying to deserialize a string to object. Is xml node like syntax, but is not an xml (as there is no root node or namespace). This is what I have so far, having this error:
<delivery xmlns=''>. was not expected
Deserialize code:
var number = 2;
var amount = 3;
var xmlCommand = $"<delivery number=\"{number}\" amount=\"{amount}\" />";
XmlSerializer serializer = new XmlSerializer(typeof(Delivery));
var rdr = new StringReader(xmlCommand);
Delivery delivery = (Delivery)serializer.Deserialize(rdr);
Delivery object:
using System.Xml.Serialization;
namespace SOMWClient.Events
{
public class Delivery
{
[XmlAttribute(AttributeName = "number")]
public int Number { get; set; }
[XmlAttribute(AttributeName = "amount")]
public string Amount { get; set; }
public Delivery()
{
}
}
}
How can I avoid the xmlns error when deserializing ?
Change the Delivery class and add information about the root element (XmlRoot attribute):
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[XmlRoot("delivery")]
public class Delivery
{
[XmlAttribute(AttributeName = "number")]
public int Number { get; set; }
[XmlAttribute(AttributeName = "amount")]
public string Amount { get; set; }
public Delivery()
{ }
}
Add the root yourself like this:
XmlRootAttribute root = new XmlRootAttribute();
root.ElementName = "delivery";
// root.Namespace = "http://www.whatever.com";
root.IsNullable = true;
// your code goes below
So, I have XML like this:
<tileset firstgid="1" name="simple_tiles" tilewidth="32" tileheight="32" tilecount="16" columns="8">
<image source="../Users/mkkek/Pictures/rpg/default_tiles_x.png" width="256" height="64"/>
</tileset>
When I'm at the tileset node, how can I access the image node and its source attribute? My code is as follows:
public void LoadMaps(ContentManager content)
{
Dictionary<string, string> mapsToLoad = InitMapsToLoad();
foreach (KeyValuePair<string, string> mapToLoad in mapsToLoad)
{
Map map = new Map();
map.Name = Path.GetFileNameWithoutExtension(mapToLoad.Value);
reader = XmlReader.Create("Content/" + mapToLoad.Value);
while(reader.Read())
{
if(reader.NodeType == XmlNodeType.Element)
{
switch(reader.Name)
{
case "tileset":
if(!Tilesets.Any(ts => ts.Name == reader.GetAttribute("name")))
{
// handling the image node here
}
break;
}
}
}
}
}
I usually prefer to use LINQ to XML because I find it's API to be much easier to use than XmlReader, a comparison between the technologies here.
If all you need is getting source attribute value from image element this can be achieved easily:
var doc = XDocument.Load("something.xml");
var root = doc.DocumentElement;
var imageElements = root.Elements("image").ToList();
foreach (var imageElement in imageElements)
{
var sourceAttribute = imageElement.Attribute("source");
var sourceValue = sourceAttribute.Value;
//do something with the source value...
}
More about basic queries in LINQ to XML here.
I will suggest to create some classes that represents the Xml structure, something like this :
[XmlRoot(ElementName = "image")]
public class Image
{
[XmlAttribute(AttributeName = "source")]
public string Source { get; set; }
[XmlAttribute(AttributeName = "width")]
public string Width { get; set; }
[XmlAttribute(AttributeName = "height")]
public string Height { get; set; }
}
[XmlRoot(ElementName = "tileset")]
public class Tileset
{
[XmlElement(ElementName = "image")]
public Image Image { get; set; }
[XmlAttribute(AttributeName = "firstgid")]
public string Firstgid { get; set; }
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "tilewidth")]
public string Tilewidth { get; set; }
[XmlAttribute(AttributeName = "tileheight")]
public string Tileheight { get; set; }
[XmlAttribute(AttributeName = "tilecount")]
public string Tilecount { get; set; }
[XmlAttribute(AttributeName = "columns")]
public string Columns { get; set; }
}
Then In some Utility class add the following method:
public static T DeserializeFromXml<T>(string xml)
{
if (string.IsNullOrEmpty(xml))
{
return default(T);
}
var serializer = new XmlSerializer(typeof(T));
T entity;
using (XmlReader reader = XmlReader.Create(new StringReader(xml)))
{
entity = (T)serializer.Deserialize(reader);
}
return entity;
}
Now you will be able to access to the Image object using the following code:
Tileset tileset=DeserializeFromXml<Tileset>(yourXmlContent);
// now you can access the image from the tileset instance 'tileset.Image.Source'
You are almost done. Add this to your code.
// handling the image node here
if (reader.ReadToDescendant("image"))
{
string source = reader.GetAttribute("source");
}
I have a Model populated and I wish to serlise to an xml document.
Due to naming conventions I have to over ride the class names for my XML document,
This is my Model(s):
[Serializable]
[XmlRoot("preferences")]
public class PreferencesModel
{
[XmlIgnore]
public string MessageToUser { get; set; }
[XmlElement(ElementName = "sectiondivider")]
public List<SectionDivider> SectionDivider { get; set; }
}
[Serializable]
[XmlRoot(ElementName = "sectiondivider")]
public class SectionDivider
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("preference")]
public List<PreferenceModel> PreferenceModel { get; set; }
}
[Serializable]
[XmlRoot("preference")]
public class PreferenceModel
{
[XmlAttribute("type")]
public string Type { get; set; }
public string Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
[XmlElement("options")]
public List<Option> Options { get; set; }
}
this is how I serialize:
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(ObjectToXmlString(obj, includeNameSpace, includeStartDocument, rootAttribute));
return xDoc;
public static string ObjectToXmlString(Object obj, bool includeNameSpace, bool includeStartDocument, XmlRootAttribute rootAttribute)
{
SpecialXmlWriter stWriter = null;
XmlSerializer xmlSerializer = default(XmlSerializer);
string buffer = null;
try
{
if (rootAttribute == null)
{
xmlSerializer = new XmlSerializer(obj.GetType());
}
else
{
xmlSerializer = new XmlSerializer(obj.GetType(), rootAttribute);
}
MemoryStream memStream = new MemoryStream();
StringWriter writer = new StringWriter();
stWriter = new SpecialXmlWriter(memStream, new UTF8Encoding(false), includeStartDocument);
if (!includeNameSpace)
{
System.Xml.Serialization.XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
//To remove namespace and any other inline
//information tag
xs.Add("", "");
xmlSerializer.Serialize(stWriter, obj, xs);
}
else
{
xmlSerializer.Serialize(stWriter, obj);
}
buffer = Encoding.UTF8.GetString(memStream.ToArray());
}
catch (Exception e)
{
string msg = e.Message;
throw;
}
finally
{
if (stWriter != null)
stWriter.Close();
}
return buffer;
}
I call it like this:
XmlDocument preferencesxml = Codec.ObjectToXml(m.SectionDivider,false,
false, new XmlRootAttribute("preferences"));
My m value is:
and my resulting XML is this:
XmlRootAttribute, as the name suggests, only applies to the root element of the XML being serialised.
You need to use XmlTypeAttribute in this context:
[XmlType("sectiondivider")]`
public class SectionDivider
{
//...
}
As an aside, the [Serializable] attribute is not relevant to XmlSerializer - it can be removed unless you need it for some other purpose.
I have the follow XML structure:
<Document>
<Sectors>
<Sector>
SectorName1
<Subsectors>
<Subsector>Subsector1</Subsector>
<Subsector>Subsector2</Subsector>
</Subsectors>
</Sector>
<Sector>
SectorName2
<Subsectors>
<Subsector>Subsector1</Subsector>
<Subsector>Subsector2</Subsector>
</Subsectors>
</Sector>
</Sectors>
</Document>
Also I have classes for deserialize:
public class MetaDataXML
{
public class SectorXML
{
[XmlArrayItem(ElementName = "Sector")]
string SectorName { get; set; }
[XmlArray]
[XmlArrayItem(ElementName = "Subsector")]
public List<string> Subsectors { get; set; }
}
public List<SectorXML> Sectors { get; set; }
}
And part of code which do deserialize:
var xRoot = new XmlRootAttribute { ElementName = "Document", IsNullable = true };
var reader = new XmlSerializer(typeof(MetaDataXML), xRoot);
var data = (MetaDataXML)reader.Deserialize(streamXML);
After deserialization I successfully get subsectors velues, but I didn't get values for SectorName. How I need to organize my structure of class that I'll get values "SectorName1" and "SectorName2" for my string SectorName property?
I found that that this case it's a "Mixed Content". How we can parse this text values?
Whilst I am not entirely sure what it is you're trying to achieve here, I've made a few modifications to your XML class and provided some sample code below that is able to retrieve all of the information about a sector, including its name and the name of all the subsectors inside it.
XML Class:
namespace DocumentXml
{
[XmlRoot("Document")]
public class Document
{
[XmlArray("Sectors")]
[XmlArrayItem("Sector")]
public Sector[] Sectors { get; set; }
}
[XmlRoot("Sector")]
public class Sector
{
[XmlAttribute("SectorName")]
public string SectorName { get; set; }
[XmlArray("Subsectors")]
[XmlArrayItem("Subsector")]
public string[] Subsectors { get; set; }
}
}
Main Program Class:
namespace DocumentXml
{
class Program
{
static void Main(string[] args)
{
var path = #"D:\sandbox\DocumentXml\DocumentXml\Sample.xml";
var serializer = new XmlSerializer(typeof(Document));
var document = serializer.Deserialize(File.OpenRead(path)) as Document;
var sectors = document.Sectors;
foreach (var s in sectors)
{
Console.WriteLine($"Sector Name: {s.SectorName}");
foreach (var ss in s.Subsectors)
{
Console.WriteLine($"Subsector Name: {ss}");
}
Console.WriteLine();
}
Console.ReadKey();
}
}
}
Sample XML:
<Document>
<Sectors>
<Sector SectorName="SectorName1">
<Subsectors>
<Subsector>Subsector1</Subsector>
<Subsector>Subsector2</Subsector>
</Subsectors>
</Sector>
<Sector SectorName="SectorName2">
<Subsectors>
<Subsector>Subsector1</Subsector>
<Subsector>Subsector2</Subsector>
</Subsectors>
</Sector>
</Sectors>
</Document>
Output:
EDIT
Since the XML structure cannot be changed, this new class will preserve the structure and also allow you to get the value in question. XmlText returns everything inside the value so a custom set had to be used to ensure that the whitespace was correctly trimmed from it.
[XmlRoot("Document")]
public class MetaDataXml
{
[XmlArray("Sectors")]
[XmlArrayItem("Sector")]
public Sector[] Sectors { get; set; }
}
[XmlRoot("Sector")]
public class Sector
{
[XmlIgnore]
private string _sectorName;
[XmlText]
public string SectorName
{
get
{
return _sectorName;
}
set
{
_sectorName = value.Trim();
}
}
[XmlArray]
[XmlArrayItem(ElementName = "Subsector")]
public List<string> Subsectors { get; set; }
}
Sample Program:
class Program
{
static void Main(string[] args)
{
var path = #"D:\sandbox\DocumentXml\DocumentXml\Sample.xml";
using (var stream = File.OpenRead(path))
{
var deserializer = new XmlSerializer(typeof(MetaDataXml));
var data = (MetaDataXml)deserializer.Deserialize(stream);
foreach (var s in data.Sectors)
{
Console.WriteLine($"Sector Name: {s.SectorName}");
foreach (var ss in s.Subsectors)
{
Console.WriteLine($"Subsector Name: {ss}");
}
Console.WriteLine();
}
}
Console.ReadKey();
}
}
Deserialization XML, XMLElement, XMLAttribute
XML:
<PET>
<RES>Correct</RES>
<PC version="1.1">
<MESSAGE conf="1">SMS</DESC>
<URL>www.google.com</URL>
</PC>
<PRU>200</PRU>
</PET>
Class:
[XmlRoot("PET")]
public class Prueba
{
[XmlElement("RES")]
public string Res { get; set; }
[XmlElement("PRU")]
public string Pru { get; set; }
//PC Attribute
//MESSAGE element AND Attribute
//URL element
}
Method:
public void Prueba()
{
Prueba p = new Prueba();
XmlSerializer serializer = new XmlSerializer(p.GetType());
using (StreamReader reader = new StreamReader("Repositories/Local/Prueba.xml"))
{
p = (Prueba)serializer.Deserialize(reader);
}
}
How to read the attribute <PC>, and MESSAGE element (attribute too) and URL?
Add
[XmlElement("PC")]
public PC pc { get; set; }
And create class for PC ,
[XmlRoot("PET")]
public class PC
{
[XmlElement("MESSAGE")]
public string MES { get; set; }
[XmlElement("URL")]
public string url { get; set; }
}