In my c# code I am trying to use XML serialization instead of xml linq. I am not sure which one is better.
But for now the issue I am having is that my node is coming twice. So this is my class structure
public class Enterprise
{
public Properties properties;
public List<Person> person;
}
This is the xml I get generated
<?xml version="1.0" encoding="iso-8859-1"?>
<enterprise>
<properties lang="nb">
<Comments>Utlasting av LMS-data</Comments>
<Datasource>SIKT</Datasource>
<Target />
<Datetime>onsdag 24. oktober 2018</Datetime>
</properties>
<person>
<Person>
<sourceid>
<Source>AD</Source>
<Id>123</Id>
</sourceid>
<Userid>mohsin</Userid>
</Person>
As you see the Person tag is coming twice. This is how it is set up
Enterprise enterprise = new Enterprise();
enterprise.properties.LanguageCode = "nb";
enterprise.properties.Comments = "Utlasting av LMS-data";
enterprise.properties.Datasource = "SIKT";
enterprise.properties.Target = "";
enterprise.properties.Datetime = DateTime.Now.ToLongDateString();
List<Person> person = new List<Person>();
person.Add(new Person
{
//sourceid = new SourceId
//{
// Id = "123",
// Source = "AD"
//},
Userid = "mohsin"
});
enterprise.person = person;
Does anyone knows the reason?
When you use List or array it will conside as `XmlArrayItem' to overcome use 'XmlElement'
public class Enterprise
{
public Properties properties;
[XmlElement("person")]
public List<Person> person;
}
Related
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>
I am new to unit testing and I am wondering what would be the best practices for unit testing xml deserialisation.
Consider the following xml:
<people>
<person id="1">
<name>Joe</name>
<age>28</age>
</person>
<person id="2">
<name>Jack</name>
<age>38</age>
</person>
</people>
And the following model class for the people:
[XmlRoot(ElementName ="people")]
public class People
{
public People() { PeopleList = new List<Person>(); }
[XmlElement("person")]
public List<Person> PeopleList { get; set; }
}
public class Person
{
[XmlAttribute("id")]
public int id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("age")]
public int Age { get; set; }
}
I deserialize the xml using:
public List<Person> GetListOfPeople()
{
People plist = new People();
string content;
using (StreamReader sr = new StreamReader(manager.Open("People.xml")))
{
var serializer = new XmlSerializer(typeof(People));
plist = (People)serializer.Deserialize(sr);
}
return plist.PeopleList;
}
What would be the best methods to unit test the GetListOfPeople method above?
If you can change your method to take an xml file as an input parameter, you can have a sample xml file created and added to your test project. Since you know the values of your xml file, you can start comparing the values directly.
Considering you'll use the sample file you provided in your test, you can verify like this:
var persons = x.GetListOfPeople("sample.xml");
Assert.AreEqual("Joe", persons[0].Name);
Assert.AreEqual(38, persons[1].Age);
If the xml file is coming to your code from some source and you think it couldn't be following your xml schema all the time, then probably you can create some sample xml files again which violate your schema and prepare tests to call your method which should throw some exception if schema is not correct.
Hope this helps.
I am creating a webservice in .net :
WebService :
public class ValidateWebService : System.Web.Services.WebService
{
[WebMethod]
public List<string> ListAllArtists1()
{
List<string> all1 = new List<string>();
all1.Add("Puneet");
all1.Add("03/07/1988");
all1.Add("Delhi");
return all1;
}
}
}
and when I browse this webservice and invoke the method, ths will return me a list in xml format like this :
<?xml version="1.0" encoding="utf-8" ?>
- <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<string>Puneet</string>
<string>03/07/1988</string>
<string>Delhi</string>
</ArrayOfString>
My problem is the format of this xml. I want the xml format like this:
<Name>1</Name>
<DOB>03/07/1988</DOB>
<State>Delhi</State>
So that I can easily read the xml by the name of its nodes.
Please help.
If you want xml in such format, then return object instead of collection of strings:
public Artist GetArtist()
{
Artist artist = new Artist();
artist.Name = "Puneet";
artist.DOB = "03/07/1988"; // consider DateTime type
artist.State = "Delhi";
return artist;
}
This will return serialized Artist instance:
<Artist>
<Name>Puneet</Name>
<DOB>03/07/1988</DOB>
<State>Delhi</State>
</Artist>
I need to take in a text document from a server and create an XML hierarchy from it in order to use it in a C# program.
It will be an organizational hierarchy. Here is the same of the text file:
EmployeeID; Employee Name; SupervisorID
1; Bob; 3
2; Mark; 1
3; Jill; 0
4; Ann ; 1
Where the above relationships would be:
Bob's boss is Jill. Mark's boss is Bob, and Jill has no boss.
I want to create an XML file from that data to look something like this:
<Employee> Jill
<Employee> Bob
<Employee> Mark </Employee>
<Employee> Ann </Employee>
</Employee>
</Employee>
I don't know if this makes sense, because I have never worked with C# or XML before.
Feel free to change how the tags are named, the main things I will need to do:
Be able to get the names of everyone supervised by the same person. (Ex: Mark would want Mark and Ann)
Be able to get the names of all supervisors above an employee (Ex: Mark's would be Bob and Jill)
Be able to get the names of all people under then (Ex: Mark would have nobody, Jill would have everybody, Bob would have Mark and Ann)
I've looked at XElement and XDoc and various tutorials and SO questions but most of the SO questions are too advanced for me at this point, and most of the tutorials aren't trying to create a hierarchy like mine.
Define a class Employee:
public class Employee{
[XmlIgnore]
public int ID{get;set;}
[XmlIgnore]
public int BossID{get;set;}
[XmlText]
public string Name{get;set;}
[XmlElement("Employee")
public Employee[] Minions{get;set;}
public SetMinions(List<Employee> list){
Minions = list.Where(e=>e.BossID==ID).ToArray();
}
}
then parse your input:
List<Employee> list = File.ReadAllLines("MyInputFile.txt")
.Select(l=>new Emplyee(){
ID = l.Split(';')[0],
Name= l.Split(';')[1],
BossID = l.Split(';')[2]}).ToList();
and then set the minions for each:
list.ForEach(e=>e.SetMinions(list));
And produce the XML like this:
XmlSerializer xser = new XmlSerializer(typeof(Employee));
xser.Serialize(File.OpenWrite("output.txt", list.First(e=>e.BossID==0)));
If it's not obvious this code is quite dirty and unreliable, add some checks and cleanups
Employee class:
public class Employee
{
public Employee()
{
Subordinates = new List<Employee>();
}
public int Id { get; set; }
public string Name { get; set; }
public int SupervisorId { get; set; }
public List<Employee> Subordinates { get; private set; }
public XElement ToXml()
{
return new XElement("Employee",
new XElement("Id", Id),
new XElement("Name", Name),
new XElement("Subordinates", Subordinates.Select(s => s.ToXml())));
}
}
And parsing logic:
// dictionary as a storage for data read from file
var Employees = new Dictionary<int,Employee>();
// file reading
var file = File.Open("Input.txt", FileMode.Open);
using (var reader = new StreamReader(file))
{
reader.ReadLine();
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split(';');
var newEmployee = new Employee { Id = int.Parse(fields[0]), Name = fields[1].Trim(), SupervisorId = int.Parse(fields[2]) };
Employees.Add(newEmployee.Id, newEmployee);
}
}
// filling Subordinates within every employee
foreach (var emp in Employees.Values)
{
if (Employees.ContainsKey(emp.SupervisorId))
Employees[emp.SupervisorId].Subordinates.Add(emp);
}
// taking first (root) employee by selecting the one with supervisorId == 0
var first = Employees.Values.First(e => e.SupervisorId == 0);
// XML generation
var xml = first.ToXml();
Result that I received from that code with your sample input:
<Employee>
<Id>3</Id>
<Name>Jill</Name>
<Subordinates>
<Employee>
<Id>1</Id>
<Name>Bob</Name>
<Subordinates>
<Employee>
<Id>2</Id>
<Name>Mark</Name>
<Subordinates />
</Employee>
<Employee>
<Id>4</Id>
<Name>Ann</Name>
<Subordinates />
</Employee>
</Subordinates>
</Employee>
</Subordinates>
</Employee>
I think that structure is better than you suggested. However, you can easily modify XML structure by modifying ToXml method within Employee class. Following one would give output you suggested:
public XElement ToXml()
{
return new XElement("Employee",
new XText(Name),
Subordinates.Select(s => s.ToXml()));
}
Result:
<Employee>Jill
<Employee>Bob
<Employee>Mark</Employee>
<Employee>Ann</Employee>
</Employee>
</Employee>
I have an XML file:
<?xml version="1.0" encoding="UTF-8"?>
<MyProducts>
<Product Name="P1" />
<Product Name="P2" />
</MyProducts>
And a C# object:
Public Class Product
{
[XmlAttribute]
Public Name {get; set;}
}
Using the .NET Serializer class now can I Deserialize the XML file into an IList without creating a MyProducts object?
So I want to somehow select only the Product elements before I serialize
There's a quick-and-dirty way to accomplish what you want - simply replace "MyProducts" with something the BCL classes are happy with - ArrayOfProduct:
string xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
//secret sauce:
xml = xml.Replace("MyProducts>", "ArrayOfProduct>");
IList myProducts;
using (StringReader sr = new StringReader(xml))
{
XmlSerializer xs = new XmlSerializer(typeof(List<Product>));
myProducts = xs.Deserialize(sr) as IList;
}
foreach (Product myProduct in myProducts)
{
Console.Write(myProduct.Name);
}
Of course, the right way would be to transform the XML document to replace the MyProducts nodes appropriately - instead of using a string replace - but this illustrates the point.
If you don't want to create a collection class for your products, you can mix some LINQ to XML with the XmlSerializer:
public static IEnumerable<T> DeserializeMany<T>(
string fileName, string descendentNodeName = null) {
descendentNodeName = descendentNodeName ?? typeof(T).Name;
var serializer = new XmlSerializer(typeof(T));
return
from item in XDocument.Load(fileName).Descendants(descendentNodeName)
select (T)serializer.Deserialize(item.CreateReader());
}
Then, to get your list:
var products = XmlSerializerUtils.DeserializeMany<Product>(fileName).ToList();
I'm not sure you're going to have much success using the XML serializer to accomplish what you need. It may be simpler for you to manually parse out the XML and map them explicitly, e.g.
XDocument xml = XDocument.Parse(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<MyProducts>
<Product Name=""P1"" />
<Product Name=""P2"" />
</MyProducts>");
foreach(var product in xml.Descendants(XName.Get("Product")))
{
var p = new Product { Name = product.Attribute(XName.Get("Name")).Value };
// Manipulate your result and add to your collection.
}
...
public class Product
{
public string Name { get; set; }
}
If you're using a file, which you most likely are for your XML, just replace the Parse method on the XDocument w/ Load and the appropriate signature.
I don't think you can instruct the serializer to spit out a IList. The serializer can create a MyProduct collection object and fill it with Products. Which sounds like what you want to do.
You can also use LINQ to query the XML document and create a list of IEnumerable as well.
// load from stream if that is the case
// this just uses a file for demonstration purposes
XDocument doc = XDocument.Load("location_of_source.xml");
// select all Product nodes from the root node and create a new Product object using
// object initialization syntax
var listOfProduct = doc.Descendants("Product")
.Select(p => new Product { Name = p.Attribute("Name").Value});
While it's novel to not create the class, doing so saves you a lot of code... You don't even have to use it except when you deserialize.
//Products declaration
[XmlRoot(ElementName = "MyProducts")]
public class MyProducts : List<Product>
{
}
public class Product
{
[XmlAttribute]
public string Name { get; set; }
}
...
[Test]
public void DeserializeFromString()
{
var xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
IList<Product> obj = Serialization<MyProducts>.DeserializeFromString(xml);
Assert.IsNotNull(obj);
Assert.AreEqual(2, obj.Count);
}
...
//Deserialization library
public static T DeserializeFromString(string serialized)
{
var byteArray = Encoding.ASCII.GetBytes(serialized);
var memStream = new MemoryStream(byteArray);
return Deserialize(memStream);
}
public static T Deserialize(Stream stream)
{
var xs = new XmlSerializer(typeof(T));
return (T) xs.Deserialize(stream);
}
The problem is that IList is not serializable so you'd have to implement your custom XmlSerializer - walk through xml nodes etc, you can however deserialize your collection into List using out of the box XmlSerializer.
Dont forget to add default constructor to your Product Class.
XmlSerializer serializer = new XmlSerializer(typeof(List<Product>));
FileStream stream = new FileStream(fileName, FileMode.Open);
var product = serializer.Deserialize(sream) as List<Product>;