How does XML Serialization know where to put the attribute? - c#

I am trying to figure out how to do XML serialization.
This is how I want my XML document too look like in the end
<course>
<name></name>
<backgroundColor></backgroundColor>
<fontColor></fontColor>
<sharingKey></sharingKey>
<task id="">
<taskName></taskName>
<description></description>
</task>
<task id="">
<taskName></taskName>
<description></description>
</task>
</course>
So far mine looks like
<?xml version="1.0" encoding="utf-8"?>
<Course xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<name>name</name>
<backgroundColor>test</backgroundColor>
<fontColor>test2</fontColor>
<sharingKey>9324bfab-6cc7-49e5-84f7-56130b8dc099</sharingKey>
<task id="first Task" />
<task id="Second task" />
</Course>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
[XmlRoot("Course")]
public class MyWrapper
{
public MyWrapper()
{
TaskList = new List<Tasks>();
}
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("backgroundColor")]
public string BackgroundColor { get; set; }
[XmlElement("fontColor")]
public string FontColor { get; set; }
[XmlElement("sharingKey")]
public Guid SharingKey { get; set; }
[XmlElement("task")]
public List<Tasks> TaskList { get; set; }
}
public class Tasks
{
[XmlAttribute("id")]
public string Id { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Tasks task = new Tasks();
task.Id = "first Task";
Tasks task2 = new Tasks();
task2.Id = "Second task";
MyWrapper wrap = new MyWrapper();
wrap.BackgroundColor = "test";
wrap.FontColor = "test2";
wrap.Name = "name";
wrap.SharingKey = Guid.NewGuid();
wrap.TaskList.Add(task);
wrap.TaskList.Add(task2);
SerializeToXML(wrap);
}
static public void SerializeToXML(MyWrapper list)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyWrapper));
TextWriter textWriter = new StreamWriter(#"C:\New folder\test.xml");
serializer.Serialize(textWriter, list);
textWriter.Close();
}
}
}
So my question is with the "id" for the task.
All I have is another class with this in it
[XmlAttribute("id")]
public string Id { get; set; }
How did it know to put this attribute in the task tag?
What happens if I wanted to have another property
[XmlElement()]
public string TaskName {get; set;}
Say I wanted to have an attribute with this element how would I make sure that the attribute would be with TaskName not with Task?

[XmlElement("task")]
public List<Tasks> TaskList { get; set; }
This part of your code told it to serialize every Tasks object in an element called "task". The serializer looks at the properties on the Tasks class and finds the Id property which you marked with [XmlAttribute("id")] so it gets serialized as an attribute to the "task" element for the object.
I'm not sure I understand your second question. You cannot add an attribute to the element because its type is a string. Instead you would have to create a new class to wrap the concept of a task name which would have a name property and whatever other properties you wanted to add to it.

Basically: XML elements can have two kinds of properties, attributes and elements. You defined an XML element task and an attribute on task called id, thus the serializer adds the attribute to task.
Now, suppose you want to add elements to reside within task -- this is also okay, as I said, XML elements can contain other elements, or have attributes. Simply define any elements you want to contain within task...within task.
If you want to have an attribute attached to a different element, you need to create a new XML element (here that corresponds to a class) and literally set it as an attribute type using the [XmlAttribute("id")] syntax.
Perhaps there's a disconnect for you here -- when you define the simplest form of element, we can call that a simpleType, and it can have values that are Strings or Integers or any kind of relatively primitive type (dates are valid, too). But if you want that same element to also have attributes, it suddenly needs to become a complexType, since it has complexContent -- it may contains both simple content, and properties, like, say, an attribute.
Take a look at how to write XML Schemas - w3schools have some excellent tutorials - I think that you'll gain a much better understanding of this whole mix of simple and complex content. Effectively, by defining an XML serialization for your classes you are also defining an XML schema; and you can in fact compile your code into such a schema. Understanding to construct the schemas will let you understand how to construct your code to generate the appropriate schemas, and additionally, understand your serialization output.

Related

Is there a way to save a list of objects in dataset in c#

I want to save a list of dates in an xml, I want to use dataset to achieve the task, I do the same to a database using Entity Framework. This allows me to access the dates using event.eventDates.start
but in the dataset I cannot achieve it.
public class Event
{
[Key]
public string id { get; set; }
public virtual ICollection<Date> eventDates { get; set; }
}
Date class
public class Date
{
public DateTime start { get; set; }
public DateTime end { get; set; }
}
When using entity framework I can access the eventDates Object using event.eventDates.start
I mapped the data from the sql database in the dataset builder the relations look like this
I want the xml file to be in this format
<?xml version="1.0" standalone="yes"?>
<db xmlns="http://tempuri.org/LocalDB.xsd">
<Event>
<id>ID</id>
<eventdates>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
</eventdates>
</Event>
</db>
Is there any way to achieve that using datasets?
I'm new to C# any help would be appreciated
This code works, based on generate xml files based on my c# classes. I would rename your classes to something else than "Event" and "Date". These names are too generic and are usually protected by the system. ICollection is not serializable. Read XML serialization of interface property. You can use DTO or you can change the collection type (i.e. with List<>) and with XML serialization attributes avoid circular references and/or disable lazy load (i.e. use eagerly load using Include method) or the risk is that you serialize the whole database.
Program.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace EFToXml
{
public class MyEvent
{
public string Id { get; set; }
public virtual List<MyDate> EventDates { get; set; }
}
public class MyDate
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var myDate1 = new MyDate
{
Start = DateTime.Now,
End = DateTime.Now.AddDays(1)
};
var eventDates = new List<MyDate> { myDate1 };
var myEvent = new MyEvent
{
Id = "1",
EventDates = eventDates
};
XmlSerializer serializer = new XmlSerializer(typeof(MyEvent));
serializer.Serialize(File.Create(#"C:\Users\<UserName>\Source\Repos\myEvents.xml"), myEvent);
}
}
}
myEvents.xml:
<?xml version="1.0"?>
<MyEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<EventDates>
<MyDate>
<Start>2019-11-24T21:52:04.5032671+01:00</Start>
<End>2019-11-25T21:52:04.5194026+01:00</End>
</MyDate>
</EventDates>
</MyEvent>
If i understand correctly, you just want to reproduce EntityFramework's behavior using DataSets.
You can achieve exactly same thing simply creating DataSet (using integrated VisualStudio designer) with proper tables, fields and relations:
later on you can access your data using code like this:
var ds = new DataSet1();
var ue = ds.UserEvents.FirstOrDefault();
var ued = ue.GetChildRows("FK_UserEvents_EventDates")
.Cast<DataSet1.EventDatesRow>();
var date = ued.FirstOrDefault().Date;
Next thing to do is serialization - it's quite easy:
Serialization example

XML deserialize to same object different element names

I am trying to deserialize an xml to an object to use. We have created templates and would like to keep the xml the same standard if possible. The problem I am trying to figure out is how to look within a standard node in the xml and all subnodes are the same object type, just with different node names.
For example:
<Account>
<AccountNumber>12345</AccountNumber>
<Balance>12.52</Balance>
<LateFee>0</LateFee>
</Account>
The Account level is always within the template, but everything below that is variable. Is there a way to deserialize all nodes within the Account level to be the same object?
Public Class AccountNode
{
Public String Name { get; set; }
Public String Value { get; set; }
}
Based on my research, it appears, they have to have a standard naming schema and then you can have an attribute to assign to the Name value. I just haven't been able to confirm that. If someone has a link that I haven't been able to find, or is knowledgeable and can confirm whether or not this is a possibility, I would like to know.
EDIT:
I have a much larger xml than listed above, so I'm trying to see how I can deserialize it.
<AccountNumber>
<KeyWord Name="Customer Account" isRegex="False" errorAllowance="10" LookFor="Customer Account">
<Rectangle>
<Left>200</Left>
<Bottom>350</Bottom>
<Right>600</Right>
<Top>690</Top>
</Rectangle>
<Relations KwName="Charges">
<Relation>above.0</Relation>
</Relations>
</KeyWord>
<Capture DataType="String" FindIfKeywordMissing="false">
<Rectangle>
<Left>200</Left>
<Bottom>350</Bottom>
<Right>600</Right>
<Top>690</Top>
</Rectangle>
<Relations anchorName="ChargeSection">
<Relation>rightOf.0</Relation>
<Relation>below.-20</Relation>
<Relation>above.20</Relation>
<Relation>width.150</Relation>
</Relations>
<Regex>Customer account\s+(\S+)</Regex>
</Capture>
</AccountNumber>
So with this one, I assume it is similar, but basically the Account Number node is the variable one and everything above and below it is standard.
You could use a surrogate [XmlAnyElement] public XElement[] AccountNodesXml property in your Account class to manually convert your AccountNode objects from and to XML nodes. Marking the property with XmlAnyElement ensures that the elements will taken verbatim from the XML:
public class Account
{
public Account() { this.AccountNodes = new List<AccountNode>(); }
[XmlIgnore]
public List<AccountNode> AccountNodes { get; set; }
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[XmlAnyElement]
public XElement[] AccountNodesXml
{
get
{
if (AccountNodes == null)
return null;
return AccountNodes.Select(a => new XElement((XName)a.Name, a.Value)).ToArray();
}
set
{
if (value != null)
AccountNodes = value.Select(e => new AccountNode { Name = e.Name.LocalName, Value = (string)e }).ToList();
}
}
}
Sample fiddle which successfully deserialized and re-serializes the following XML:
<Account>
<AccountNumber>12345</AccountNumber>
<Balance>12.52</Balance>
<LateFee>0</LateFee>
</Account>

Reading large XML files with C#

I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}

How to exclude a property from DataContractSerialization When It Has No Data?

I'm using DataContractSerializer in my Web API application and in my action I'm returning a Data Type as below:
public class Event
{
public string Name {get; set;}
public IList<Division> Divisions {get;set;}
}
When serialized it's returning the below xml:
<Event xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07
/EventTypeNameSpace">
<Name>some name</Name>
<Divisions i:nil="true" />
</Event>
1) Why is it returning two xmlns:i and xmlns attributes? how can they be excluded?
2) How can I exclude the Divisions from the xml when it's null?
1: the "http://schemas.datacontract.org/2004/07" is the default namespace used by types serialized by data-contract serializer; if you don't like that - change your contract; the "http://www.w3.org/2001/XMLSchema-instance" defines "nil" as a special value
2: by defining the contract properly
[DataContract(Namespace="")]
public class Event
{
[DataMember]
public string Name { get; set; }
[DataMember(EmitDefaultValue=false)]
public IList<Division> Divisions { get; set; }
}
However: I should add - if you want tight control over what the layout looks like, you should probably be using XmlSerializer, not DataContractSerializer

Adding attributes to an XML node

How can I create an xml file dynamically, with the following structure?
<Login>
<id userName="Tushar" passWord="Tushar">
<Name>Tushar</Name>
<Age>24</Age>
</id>
</Login>
I am not able to create the attributes inside the id tag (i.e. userName="" and passWord="").
I am using C# in a windows application.
Some Important namespace that you might require is
using System.Xml;
using System.IO;
Well id isn't really the root node: Login is.
It should just be a case of specifying the attributes (not tags, btw) using XmlElement.SetAttribute. You haven't specified how you're creating the file though - whether you're using XmlWriter, the DOM, or any other XML API.
If you could give an example of the code you've got which isn't working, that would help a lot. In the meantime, here's some code which creates the file you described:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("Login");
XmlElement id = doc.CreateElement("id");
id.SetAttribute("userName", "Tushar");
id.SetAttribute("passWord", "Tushar");
XmlElement name = doc.CreateElement("Name");
name.InnerText = "Tushar";
XmlElement age = doc.CreateElement("Age");
age.InnerText = "24";
id.AppendChild(name);
id.AppendChild(age);
root.AppendChild(id);
doc.AppendChild(root);
doc.Save("test.xml");
}
}
There is also a way to add an attribute to an XmlNode object, that can be useful in some cases.
I found this other method on msdn.microsoft.com.
using System.Xml;
[...]
//Assuming you have an XmlNode called node
XmlNode node;
[...]
//Get the document object
XmlDocument doc = node.OwnerDocument;
//Create a new attribute
XmlAttribute attr = doc.CreateAttribute("attributeName");
attr.Value = "valueOfTheAttribute";
//Add the attribute to the node
node.Attributes.SetNamedItem(attr);
[...]
The latest and supposedly greatest way to construct the XML is by using LINQ to XML:
using System.Xml.Linq
var xmlNode =
new XElement("Login",
new XElement("id",
new XAttribute("userName", "Tushar"),
new XAttribute("password", "Tushar"),
new XElement("Name", "Tushar"),
new XElement("Age", "24")
)
);
xmlNode.Save("Tushar.xml");
Supposedly this way of coding should be easier, as the code closely resembles the output (which Jon's example above does not). However, I found that while coding this relatively easy example I was prone to lose my way between the cartload of comma's that you have to navigate among. Visual studio's auto spacing of code does not help either.
You can use the Class XmlAttribute.
Eg:
XmlAttribute attr = xmlDoc.CreateAttribute("userName");
attr.Value = "Tushar";
node.Attributes.Append(attr);
If you serialize the object that you have, you can do something like this by using "System.Xml.Serialization.XmlAttributeAttribute" on every property that you want to be specified as an attribute in your model, which in my opinion is a lot easier:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public class UserNode
{
[System.Xml.Serialization.XmlAttributeAttribute()]
public string userName { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string passWord { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
public class LoginNode
{
public UserNode id { get; set; }
}
Then you just serialize to XML an instance of LoginNode called "Login", and that's it!
Here you have a few examples to serialize and object to XML, but I would suggest to create an extension method in order to be reusable for other objects.

Categories