XML Deserialization doesn't work - c#

public class PersistableObject
{
public static T Load<T>(string fileName) where T : PersistableObject, new()
{
T result = default(T);
using (XmlReader reader = XmlReader.Create(fileName))
{
result = new XmlSerializer(typeof(T)).Deserialize(reader) as T;
}
return result;
}
public void Save<T>(string fileName) where T : PersistableObject
{
using (FileStream stream = new FileStream(fileName, FileMode.CreateNew))
{
new XmlSerializer(typeof(T)).Serialize(stream, this);
}
}
}
public class DatabaseConfiguration : PersistableObject
{
public string Host { get; set; }
public string Schema { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
I load the XML using the following code:
var configuration = PersistableObject.Load<DatabaseConfiguration>("Database.xml");
However, configuration's properties are null. This is Database.xml:
<?xml version="1.0"?>
<DatabaseConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
<Host>localhost</Host>
<Schema>chromium</Schema>
<Username>root</Username>
<Password></Password>
</DatabaseConfiguration>
They stay null for some reason and are not assigned anything. Why's that?

Your Database.xml content is incorrect, specifically its second line that closes the DatabaseConfiguration element.
Replace it with:
<?xml version="1.0"?>
<DatabaseConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<Host>localhost</Host>
<Schema>chromium</Schema>
<Username>root</Username>
<Password></Password>
</DatabaseConfiguration>

Related

How to deserialize XML to get the data from tags?

I have below XML stored in a string RequestPassengerXML. How can I deserialize it so that I can get the tags information which I can assign it to my class members.
<?xml version="1.0" encoding="utf-16"?>
<Passenger xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>John Vaz</Name>
<Email>john#gmail.com</Email>
<cardNumber>CB2390VT</cardNumber>
</Passenger>
I have created a class with below details
public class PassengerDetails
{
public PassengerDetails();
public string Name { get; set; }
public string Email { get; set; }
public string CardNumber{ get; set; }
}
public static XElement ToXElement<T>(this object obj)
{
using (var memoryStream = new MemoryStream())
{
using (TextWriter streamWriter = new StreamWriter(memoryStream))
{
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(streamWriter, obj);
return XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
}
}
}
public static T FromXElement<T>(this XElement xElement)
{
var xmlSerializer = new XmlSerializer(typeof(T));
return (T)xmlSerializer.Deserialize(xElement.CreateReader());
}
Usage:
XElement element = PassengerDetails.ToXElement<PassengerDetails>();
var newMyClass = element.FromXElement<PassengerDetails>();

Parse XML response in C#

I got an XML response as:
<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns="site.com/api" xmlns:xsi="site.org/2001/XMLSchema-instance" xsi:schemaLocation="site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd">
<credentials token="xxxxxx-xxxxxx-xxxxxx">
<site id="xxxxxx-xxxxxx-xxxxxx" contentUrl="sitename"/>
<user id="xxxxxx-xxxxxx-xxxxxx"/>
</credentials>
</tsResponse>
How can I parse this response to get the token value inside credentials?
By using LINQ to XML.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<tsResponse xmlns='site.com/api' xmlns:xsi='site.org/2001/XMLSchema-instance' xsi:schemaLocation='site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd'>
<credentials token='xxxxxx-xxxxxx-xxxxxx'>
<site id='xxxxxx-xxxxxx-xxxxxx' contentUrl='sitename'/>
<user id='xxxxxx-xxxxxx-xxxxxx'/>
</credentials>
</tsResponse>");
XNamespace ns1 = xdoc.Root.GetDefaultNamespace();
string token = xdoc.Descendants(ns1 + "credentials")
.FirstOrDefault()?.Attribute("token").Value;
Console.WriteLine("token = {0}", token);
}
Output
token = xxxxxx-xxxxxx-xxxxxx
I think that the simplest way is to use LINQ to XML's XDocument:
using System.Xml;
using System.Xml.Linq;
/* ... */
var xml = #"<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns=""site.com/api"" xmlns:xsi=""site.org/2001/XMLSchema-instance"" xsi:schemaLocation=""site.com/api help.site.com/samples/en-us/rest_api/ts-api_3_4.xsd"">
<credentials token=""xxxxxx-xxxxxx-xxxxxx"">
<site id=""xxxxxx-xxxxxx-xxxxxx"" contentUrl=""sitename""/>
<user id=""xxxxxx-xxxxxx-xxxxxx""/>
</credentials>
</tsResponse>";
var xmlns = XNamespace.Get("site.com/api");
var token = XDocument.Parse(xml)
.Element(xmlns + "tsResponse")?
.Element(xmlns + "credentials")?
.Attribute("token")?
.Value;
If you put the XML string that you get into the variable xmlToParse, you can do:
TsResponse result = _xmlParser.Deserialize<TsResponse>(xmlToParse);
Where _xmlParser is an instance of the class:
using System.Xml.Serialization;
namespace yourNameSpace
{
public class XmlParser
{
public T Deserialize<T>(string input) where T : class
{
XmlSerializer ser = new XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(input))
{
return (T)ser.Deserialize(sr);
}
}
}
}
You also need to model the whole XML object as a class in C# (XmlObjects.Credentials in my example) using the for example the page https://json2csharp.com/code-converters/xml-to-csharp which gives you:
// using System.Xml.Serialization;
// XmlSerializer serializer = new XmlSerializer(typeof(TsResponse));
// using (StringReader reader = new StringReader(xml))
// {
// var test = (TsResponse)serializer.Deserialize(reader);
// }
[XmlRoot(ElementName="site")]
public class Site {
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
[XmlAttribute(AttributeName="contentUrl")]
public string ContentUrl { get; set; }
}
[XmlRoot(ElementName="user")]
public class User {
[XmlAttribute(AttributeName="id")]
public string Id { get; set; }
}
[XmlRoot(ElementName="credentials")]
public class Credentials {
[XmlElement(ElementName="site")]
public Site Site { get; set; }
[XmlElement(ElementName="user")]
public User User { get; set; }
[XmlAttribute(AttributeName="token")]
public string Token { get; set; }
}
[XmlRoot(ElementName="tsResponse")]
public class TsResponse {
[XmlElement(ElementName="credentials")]
public Credentials Credentials { get; set; }
[XmlAttribute(AttributeName="xmlns")]
public string Xmlns { get; set; }
[XmlAttribute(AttributeName="xsi")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName="schemaLocation")]
public string SchemaLocation { get; set; }
}
, then you can go with
result.Credentials.Token

Unable to convert XML to C# Object List

I'm trying to convert the XML data to an object list, but it throws an error.
XML
<?xml version="1.0" encoding="utf-8" ?>
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>
C#
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(List<ServerDetails>));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
List<ServerDetails> list = (List<ServerDetails>)serializer.Deserialize(stream);
//Exception here
}
}
ERROR
Inner Exception : <Servers xmlns=''> was not expected.
There is an error in XML document (2,2)
I tried adding the [Serializabe] and [XMLElement] attributes to the class,
and also xmlns="http://www.example.com/xsd/ServerDetails" in the XML
but that did not help.
You have ServerDetails as your class name and in the xml the tag name is different, Try something like this.
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
public class ServerList
{
[XmlArray("Servers")]
[XmlArrayItem("Server", Type = typeof(ServerDetails))]
public ServerDetails[] Servers { get;set;}
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(ServerList));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
var list = (ServerList)serializer.Deserialize(stream);
//Exception here
}
}
I used to use XmlSerializer a lot, but I totally stopped using it because you are forced to create your object structure fitting the xml structure. That makes it hard to maintain. Also XmlSerializer has some serious memory leaks.
If you don't mind, I would suggest to switch to XElement
public IEnumerable<ServerDetails> GetServers(string file)
{
using (var stream = File.Open(file, FileMode.Open, FileAccess.Read))
return GetServers(stream);
}
public IEnumerable<ServerDetails> GetServers(Stream stream)
{
var root = XElement.Load(stream);
return GetServers(root);
}
public IEnumerable<ServerDetails> GetServers(XElement root)
{
foreach (var server in root.Elements("Server"))
{
yield return new ServerDetails
{
ServerName = (string)server.Element("ServerName"),
ServerIP = (string)server.Element("ServerIP"),
};
}
}
Please note that you have to reference System.Xml.Linq
For your convenience here is a test case.
[TestMethod]
public void CanReadServers()
{
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + #"
<Servers>
<Server>
<ServerName>STAGING</ServerName>
<ServerIP>XXX.XXX.XX.X</ServerIP>
</Server>
</Servers>";
IEnumerable<ServerDetails> servers;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
servers = GetServers(stream).ToList();
Assert.AreEqual(1, servers.Count());
Assert.AreEqual("STAGING", servers.ElementAt(0).ServerName);
Assert.AreEqual("XXX.XXX.XX.X", servers.ElementAt(0).ServerIP);
}
Use XmlElement Notation to specify the element name.
public class Servers
{
[XmlElement("Server")]
public ServerDetails[] ServersDetails { get; set; }
}
public class ServerDetails
{
public string ServerName { get; set; }
public string ServerIP { get; set; }
}
private void GetXMLData()
{
XmlSerializer serializer = new XmlSerializer(typeof(Servers));
using (FileStream stream = File.OpenRead("D:\\Resource.xml"))
{
Servers list = (Servers)serializer.Deserialize(stream);
//Exception here
}
}

XmlSerializer/Deserialize in C#

I created this class where it'll append to the XML called Email Alerts h1, body and ids. However, im having trouble running this file from the start (when i call LoadFromFile), when the file does not exist - so we'll have to create a file and start logging it.
public class EMailAlert
{
public string h1 { get; set; }
public string body { get; set; }
public string idList { get; set; }
public void Save(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Create))
{
var XML = new XmlSerializer(typeof(EMailAlert));
XML.Serialize(stream, this);
}
}
public static EMailAlert LoadFromFile(string fileName)
{
if (!File.Exists(fileName))
{
var file = new FileInfo(fileName);
file.Directory.Create();
var xmlFile = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Email Logger"));
xmlFile.Add(new XElement("EmailAlerts"));
xmlFile.Save(fileName);
}
using (var stream = new FileStream(fileName, FileMode.Open))
{
var XML = new XmlSerializer(typeof(EMailAlert));
return (EMailAlert)XML.Deserialize(stream);
}
}
}
When i run this code (there are no xml file - so it creates an XML file and then it throws this error {"<EmailAlerts xmlns=''> was not expected."}
Here is how the xml file looks like
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Email Logger-->
<EmailAlerts />
Not sure why its not sending back an empty EmailAlert when i call LoadFromFile.
You need a collectiontype for your all your EMailAlerts to be serialized as valid xml.
The following code does that. By creating a static helper class that holds a static List of all our EmailAlerts. It also has the Load and Save methods which reads and writes the file and use the property Alerts to get or store the EmailAlerts.
You can use attributes on your EmailAlert class if you want to control serialization. See this answer how you could do that.
public static class EMailAlerts
{
static XmlSerializer XML = new XmlSerializer(typeof(List<EMailAlert>));
public static List<EMailAlert> Alerts { get; private set; }
public static void Save(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Create))
{
XML.Serialize(stream, Alerts);
}
}
public static void LoadFromFile(string fileName)
{
if (!File.Exists(fileName))
{
Alerts = new List<EMailAlert>();
}
else
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
Alerts = (List<EMailAlert>)XML.Deserialize(stream);
}
}
}
}
public class EMailAlert
{
public string h1 { get; set; }
public string body { get; set; }
public string idList { get; set; }
}
class Program
{
static void Main(string[] args)
{
EMailAlerts.LoadFromFile("tmp.xml");
EMailAlerts.Alerts.Add(new EMailAlert{ body="foo"});
EMailAlerts.Save("tmp.xml");
}
}

How to map an object to an existing XML file in C# .NET?

I have this existing XML file serving as a template with NO data, just simple nodes... here's a sample:
<?xml version="1.0" encoding="utf-8" ?>
<catalog>
<cd>
<title />
<artist />
<country />
<company />
<price />
<year />
</cd>
</catalog>
Now I have created a similar class for it.
public class Cd
{
public string Title { get; set; }
public string Artist { get; set; }
public string Country { get; set; }
public string Company { get; set; }
public string Price { get; set; }
public string Year { get; set; }
}
The purpose is this:
Put values on the properties of the var cd = new Cd(); object
Get the existing XML file (template) then pass the values in it (like mapping the object to the existing XML)
Transform the XML template(with values) into XSLT to become HTML
I think that's all.
How to properly do this?
Thanks a lot!
You can use serialization to achieve (1) and (2)
[Serializable]
public class Cd
{
public string Title { get; set; }
public string Artist { get; set; }
public string Country { get; set; }
public string Company { get; set; }
public string Price { get; set; }
public string Year { get; set; }
}
now in order to create an xml from an object use:
public static string SerializeObject<T>(this T obj)
{
var ms = new MemoryStream();
var xs = new XmlSerializer(obj.GetType());
var xmlTextWriter = new XmlTextWriter(ms, Encoding.UTF8);
xs.Serialize(xmlTextWriter, obj);
string serializedObject = new UTF8Encoding().GetString((((MemoryStream)xmlTextWriter.BaseStream).ToArray()));
return serializedObject;
}
in order to create an object from XML use:
public static T DeserializeObject<T>(this string xml)
{
if (xml == null)
throw new ArgumentNullException("xml");
var xs = new XmlSerializer(typeof(T));
var ms = new MemoryStream(new UTF8Encoding().GetBytes(xml));
try
{
new XmlTextWriter(ms, Encoding.UTF8);
return (T)xs.Deserialize(ms);
}
catch
{
return default(T);
}
finally
{
ms.Close();
}
}
I would create class:
class catalog
{
public CD cd {get;set;}
}
Here is serialization and deserealization helper:
public class Xml
{
public static string Serialize<T>(T value) where T : class
{
if (value == null)
{
return string.Empty;
}
var xmlSerializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter))
{
xmlSerializer.Serialize(xmlWriter, value);
return stringWriter.ToString();
}
}
public static T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (TextReader reader = new StringReader(xml))
{
result = (T) serializer.Deserialize(reader);
}
return result;
}
}
Simply call:
catalog catalogObject = Xml.Deserialize<catalog>(xmlCatalogString);
I suspect you also will need to put some attributes on properties like XmlElement(ElementName = "title") because it is case sensitive.
MSDN

Categories

Resources