Deserialize CCTray Content XML to list of objects - c#

I am trying to deserialize the XML contents of the CCTray.xml from my build server with c#.
Tje XML content has a parent 'Projects' element with an array of 'Project' elements below. Each project element has multiple attributes.
I created a Projects model:
[XmlRoot("Projects")]
public class ProjectCollection
{
public ProjectCollection() { Projects = new List<Project>(); }
[XmlArray("Project")]
public List<Project> Projects { get; set; }
}
And a model for each project:
public class Project
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Activity")]
public string Activity { get; set; }
[XmlElement("lastBuildStatus")]
public string LastBuildStatus { get; set; }
[XmlElement("lastBuildLabel")]
public string LastBuildLabel { get; set; }
[XmlElement("WebUrl")]
public string WebUrl { get; set; }
[XmlElement("LastBuildTime")]
public DateTimeOffset LastBuildTime { get; set; }
}
I try and deserialize the response from HttpClient like:
var xmlStream = await response.Content.ReadAsStreamAsync();
var serializer = new XmlSerializer(typeof(ProjectCollection));
var projects = (ProjectCollection)serializer.Deserialize(xmlStream);
The result of which is an empty ProjectCollection object.
I'm know I must be mapping the XML to my object incorrectly, but I can't see where its wrong.
A sample of the XML being returned is:
<?xml version="1.0" encoding="utf-8"?>
<Projects>
<Project name="Company.Project1.Api :: Build" activity="Sleeping" lastBuildStatus="Success" lastBuildLabel="1.0.0.203" lastBuildTime="2017-03-21T10:56:53" webUrl="http://build-company.com:8153/go/pipelines/Company.Project1.Api/203/Build/1" />
<Project name="Company.Project2.Api :: Build :: Build" activity="Sleeping" lastBuildStatus="Success" lastBuildLabel="1.0.0.203" lastBuildTime="2017-03-21T10:56:53" webUrl="http://build-company.com:8153/go/tab/build/detail/Company.Project2.Api/203/Build/1/Build" />
</Projects>
What do I need to do deserialize this XML to my object correctly?
Thanks

Your class structure does not fit the data you get. With your class structure the XML struture should look different. The root needs an other element called Project. This element contains your items.
<?xml version="1.0" encoding="utf-8"?>
<Projects>
<Project>
<Project name="Company.Project1.Api :: Build" activity="Sleeping" lastBuildStatus="Success" lastBuildLabel="1.0.0.203" lastBuildTime="2017-03-21T10:56:53" webUrl="http://build-company.com:8153/go/pipelines/Company.Project1.Api/203/Build/1" />
<Project name="Company.Project2.Api :: Build :: Build" activity="Sleeping" lastBuildStatus="Success" lastBuildLabel="1.0.0.203" lastBuildTime="2017-03-21T10:56:53" webUrl="http://build-company.com:8153/go/tab/build/detail/Company.Project2.Api/203/Build/1/Build" />
</Project>
</Projects>
To get the data, based on your XML structure, correctly you have to use a simple root like
[XmlRoot("Projects")]
public class ProjectCollection : List<Project>
{
}
Also you need to update all properties of your Project. The XML Project does not have any elements, only XML attributes are used to store data. This means you need to use XmlAttribute instead of XmlElement. Also note that the names are case sensitive, which is also not correct in your code.
[XmlAttribute("name")]
public string Name { get; set; }
//...

Related

XML Config, How can I read a configuration file with a list/array of values rather than a single value

If I have a configuration file with the following list of values in the configuration.
The configuration file is an xml file...
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<Employees foor="bar">
<Employee name="aaa" surname="bbb"/>
<Employee name="ddd" surname="eee"/>
<Employee name="fff" surname="ggg"/>
</Employees>
</configuration>
Using Microsoft.Extensions.Configuration, I try to load the xml as follow
public class Employee
{
public string Name{get;set;}
public string Surname{get;set;}
}
...
public class Employees
{
public List<Employee> Employees{ get; set;}
public string Foo {get; set; }
}
...
var configurationBuilder = new ConfigurationBuilder()
AddXmlFile(path: "\\MyConfig.config")
.Build();
var employees = configurationBuilder.GetSection("Employees").Get<Employees>()
...
But the list returned is null, I am able to read single values if I move them up one level, but I would like to read a list of values inside my list.
It also does not work if I have a class within a class
For example
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<Outer foor="bar">
<Inner name="aaa" surname="bbb"/>
</Outer>
</configuration>
Scenario 1 - keeping the Employees xml wrapper element
In order to load the Employee xml elements, the corresponding property on the wrapper class must have the same name; so change Employees to Employee.
Lets also correct that foor attribute in the xml from <Employees foor="bar">
to <Employees foo="bar"> since the Employees class has a Foo property.
public class Employees
{
public List<Employee> Employee { get; set;}
public string Foo { get; set; }
}
Below shows the result of retrieving the employees section.
var employees = configurationBuilder.GetSection("Employees").Get<Employees>();
If you don't like the singular Employee property, then you can make that one private, but have to take that into account on retrieval.
You then define a public Employees as a 'pass through' property.
Note that you can't have an Employees class and a property with that same name because of an CS0542 compile error, which is to be fixed by renaming that class, to e.g. Staff.
public class Staff
{
private List<Employee> Employee { get; set; }
public List<Employee> Employees => Employee;
public string Foo { get; set; }
}
var employees = configurationBuilder.GetSection("Employees")
.Get<Staff>(o => o.BindNonPublicProperties = true);
Scenario 2 - not using an Employees xml wrapper element
Declare the xml as below.
<configuration>
<Employee name="aaa" surname="bbb" />
<Employee name="ddd" surname="eee" />
<Employee name="fff" surname="ggg" />
</configuration>
Retrieve the employees as a list of Employee.
var employees = configurationBuilder.GetSection("Employee").Get<List<Employee>>();
This is one of the way, you can do it as shown:
<util:list id="myColours" value-type="java.lang.String">
<value>red</value>
<value>green</value>
<value>blue</value>
<value>yellow</value>
<value>brown</value>
<value>orange</value>
</util:list>

xml Deserialize a list of objects containing a list of objects in c#

I'm trying to deserialize a list of objects that in turn contain lists of other object. What I've got is the following.
The actual deserializing is done by the framework in a ApiContoller httpPost request. The endpoint looks like this:
[HttpPost]
public async Task<HttpResponseMessage> MethodCall([FromBody] XmlEntries entries)
{
....
}
The XmlEntries class looks like this:
[XmlRoot("Root")]
public class XmlEntries
{
[XmlArrayItem("XmlEntry")]
public List<XmlEntry> XmlEntries{ get; set; }
public XmlEntries()
{
XmlEntries = new List<XmlEntry>();
}
public XmlEntries(IEnumerable<XmlEntry> entries)
{
XmlEntries= entries.ToList();
}
}
The XmlEntry class look like this:
public class XmlEntry
{
[XmlArrayItem("XmlSubEntry")]
public List<XmlSubEntry> XmlSubEntries{ get; set; }
}
and the XmlSubEntry looks like this.
public class XmlSubEntry
{
string AttributeOne{ get; set; }
int? AttributeTwo{ get; set; }
}
I've been using fiddler to send the following XML
<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<XmlEntries>
<XmlEntry>
<XmlSubEntries>
<XmlSubEntry>
<AttributeOne>P</AttributeOne>
<AttributeTwo>8</AttributeTwo>
</XmlSubEntry>
<XmlSubEntry>
<AttributeOne>S</AttributeOne>
<AttributeTwo>26</AttributeTwo>
</XmlSubEntry>
</XmlSubEntries>
</XmlEntry>
</XmlEntries>
</Root>
My problem is that the attributes of XmlSubEntry never gets correctly serialized. When i debug the MethodCall in apiController entries will be a list containing 1 XmlEntry with XmlSubEntries being a list of 2 XmlSubEntry, but the attributes (AttributeOne and AttributeTwo) are always null.
I have tried annotating the classes In all ways I can thing of but I still won't get the attributes to serialize correctry.
Is there any XML-ninjas around that can help me figure out what I'm doing wrong?
The best tip I can give you is to do this in reverse - add some data and serialise it to XML, see what it looks like. That will usually point to where you're wrong.
In this case, however, you're very close. The only issue you have is that you can't serialise private properties, so make them public:
public class XmlSubEntry
{
public string AttributeOne { get; set; }
public int? AttributeTwo { get; set; }
}

XML Deseriazlier exception C#

I'm using C# (WPF).
I have the following XML file:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FulllPath>c:\file1.txt</FullPath>
<FulllPath>c:\file2.txt</FullPath>
<FulllPath>c:\file3.txt</FullPath>
</Apps>
</ApplicationToRun>
And i try to deseriazlier the xml file.My Code:
mlSerializer xs = new XmlSerializer(typfof(MyXMLClass));
StringReader st = new StringReader(xmlPath);
MyXMLClass apps = (MyXMLClass)xs.Deserialize(st); //Exception - System.Windows.Markup.XamlParseException
Inside the inner exception: System.InvalidOperationException :There is an error in XML document (1.1) ...
My Classes:
[XmlRootAttriute("ApplicationToRun:)
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML {get;set;}
[XmlElement("Apps")]
public FullPath [] fullPath {get;set;}
}
public class FullPath
{
public stirng fullPathApp {set;get;}
}
Where is my mistake?
Thanks!
The XML is badly formed, but all it takes is a bit of cleaning up:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationToRun IsFromXML="true">
<Apps>
<FullPath>foo</FullPath>
<FullPath>bar</FullPath>
</Apps>
</ApplicationToRun>
Then assign the correct XML attributes to your class:
[XmlRootAttribute("ApplicationToRun")]
public class MyXMLClass
{
[XmlAttribute]
public bool IsFromXML { get; set; }
[XmlArray("Apps")]
[XmlArrayItem("FullPath")]
public string[] fullPath { get; set; }
}
There's a typo in the elements. The opening elements have 3 Ls (FulllPath) but the closing ones have 2 Ls (as they should) that's causing the XML error

Object reference not set to an instance of an object - LINQ

I am totally stumped on this one. I am getting a "Object reference not set to an instance of an object." error - but I can't figure out why. This is the code that I have:
public class PlayerProfile
{
public List<Profile> PlayerINfo = new List<Profile>();
public void LoadProfiles(string path)
{
XDocument xmlDoc = XDocument.Load(path);
PlayerINfo = new List<Profile>();
// This is where I get the error:
PlayerINfo = (from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
select new Profile
{
Name = (string)profiles.Element("Name"),
Sex = (string)profiles.Element("Sex"),
Avatar = (string)profiles.Element("Avatar").Attribute("path") ?? "",
Created = (DateTime)profiles.Element("Created")
}).ToList();
}
}
Here is my Profile class:
public class Profile
{
public string Name { get; set; }
public string Sex { get; set; }
public string Avatar { get; set; }
public DateTime Created { get; set; }
}
EDIT - Adding the XML file code:
<?xml version="1.0" encoding="utf-8"?>
<OnlineProfile>
<Player>
<Name>Stacey</Name>
<Sex>Female</Sex>
<Avatar path="/images/Picture.png" />
<Ratio>
<Win>0</Win>
<Loss>0</Loss>
<Abandoned>0</Abandoned>
</Ratio>
<Created>6/19/2011</Created>
</Player>
</OnlineProfile>
from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
This is the problem - OnlineProfile is your root element, just do
from profiles in xmlDoc.Root.Elements("Player")
Do this: from profiles in xmlDoc.Element("OnlineProfile").Elements("Player") instead of from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
From the XML you posted "OnlineProfile" is your Root element, so the child elements you expect arent there.
Tried this out + solved this in visual studio. I see the people above beat me to it. Another way to achieve this is:
xmlDoc.Element("OnlineProfile").Elements("Player")
This is what I posted prior to the Xml becomming available...
here's a good candidate for that error
(string)profiles.Element("Avatar").Attribute("...
").Attribute would cause an error. You need to check for null.
e.g.
= profiles.Element("Avatar") != null ? (string)profiles.Element("Avatar").Attribute("... : null;
do you definitely have an element called Avatar in your Xml file. Did the file load in correctly?

stuck binding xml to Model Class

I am experimenting with using xml as a database for small CMS, like gallery or staff profiles
etc
however being all subsonic minded i am stuck on how i bind my xml document to a modelclass
so that i can then use that class for strongly typed views:
here is my model class:
[XmlRoot("employee")]
public class EmployeesModel
{
[Required]
[DisplayName("Name: ")]
[XmlElement("employeeName")]
public string employeeName { get; set; }
[Required]
[DisplayName("Position: ")]
[XmlElement("employeePosition")]
public string employeePosition { get; set; }
[Required]
[DisplayName("Description")]
[XmlElement("employeeDescription")]
public string employeeDescription { get; set; }
[Required]
[DisplayName("Photo: ")]
[XmlElement("employeePhoto")]
public string employeePhoto { get; set; }
[Required]
[DisplayName("ID: ")]
[XmlElement("employeeID")]
public int employeeID { get; set; }
}
and here is my code:
XDocument xmlDoc = XDocument.Load(Server.MapPath("~/App_Data/employees.xml"));
var model = (from xml in xmlDoc.Descendants("employee")
select xml) as IEnumerable<EmployeesModel>;
return View(model);
the XML
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee>
<employeeName>David parker</employeeName>
<employeePosition>Senior Web Developer</employeePosition>
<employeeDescription>This is a test description<br>feele free to add something here.</employeeDescription>
<employeePhoto>mypic.jpg</employeePhoto>
<employeeID>1</employeeID></employee></employees>
the xml side work but model is always empty, however i get no runtime erros when trying to bind, i know there is more i should do here but i need some help.
for clarity i am using asp.net mvc 2 rc 2
thanks
You need to deserialize the XML into objects. You cannot simply cast XML as objects. When you say as IEnumerable<EmployeesModel>, you'll get a null since the types aren't compatible. Your code could look something like this:
var serializer = new XmlSerializer(typeof(EmployeesModel));
var model =
from xml in xmlDoc.Descendants("employee")
select serializer.Deserialize(xml.CreateReader()) as EmployeesModel;
Another option you could consider is to project the XElements into EmployeesModel objects, like this:
var model =
from xml in xmlDoc.Descendants("employee")
select new EmployeesModel {
employeeName = (string)xml.Element("employeeName"),
employeePosition = (string)xml.Element("employeePosition"),
employeeDescription = (string)xml.Element("employeeDescription"),
employeePhoto = (string)xml.Element("employeePhoto"),
employeeID = (int)xml.Element("employeeID"), };
As you can see, that can get tedious. However, it may be appropriate. If your XML file represents all employee data but your view shows only a subset of the data or the data in a different structure, you probably don't want your view model to be a direct copy of the contents of your data store.
If you want make binding xml to model class, you can use templay on codeplex.
Further you can do some process on your model class and their childrens.
https://templay.codeplex.com/

Categories