I have the following materials.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<dictionary>
<Sand>
<id>1000</id>
<name>Sand</name>
<state>Dust</state>
<color>#FAFAFAFF</color>
</Sand>
<Water>
<id>2000</id>
<name>Water</name>
<color>#1CA3EC64</color>
<state>Liquid</state>
</Water>
<Gas>
<color>#5F6A0032</color>
<id>3000</id>
<name>Gas</name>
<state>Gas</state>
</Gas>
</dictionary>
A single material is represented with the following class:
public class Material {
public readonly int id;
public readonly string name;
public Color color;
public MaterialState state;
}
I need a generic SerializableDictionary class that can be used like this:
var materials = new SerializableDictionary<Material>();
//...serialization stuff
materials["Rock"].color;
Is this achievable?
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Drawing;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Material> materials = doc.Root.Elements()
.Select(x => new Material()
{
id = (int)x.Element("id"),
name = (string)x.Element("name"),
color = Color.FromArgb(int.Parse(((string)x.Element("color")).Substring(1), NumberStyles.HexNumber)),
state = (MaterialState)Enum.Parse(typeof(MaterialState),(string)x.Element("state"))
}).ToList();
Dictionary<string, Material> dict = materials
.GroupBy(x => x.name, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public enum MaterialState
{
Dust,
Liquid,
Gas
}
public class Material
{
public int id { get;set; }
public string name { get;set;}
public Color color { get;set;}
public MaterialState state { get;set;}
}
}
Related
I have a question to which I basically know the answer.I would like to know why is newly created List<int> empty, how come it's empty even though I filled it with values? I could do it easily to create inside class Vysvedceni(ReportCard) list and then in Main class that I could create a separate field and then just add it to the Dictionary<Enum,List<int>> ...
Something like that:
List<Enum,List<int>> array = new ...
array.Add(1);
array.Add(2);
array.Add(5);
dic[Subject].Add(array);
But I wanted to do it differently:
Student student = new Student("Jan", "Novak",new Vysvedceni());
student.Vysvedceni.Znamky.Add(Vysvedceni.Subjects.math, new List<int>() {1,1,2,3,4,3});
My goal is simple, print Dictionary with Student and his/her marks.
My native language is not English, I wanted to translate the code, but I'm afraid I'll make a mess of it if you copy the code. At least I've added a small translation.
I've also added my comment where problem is or what methods do.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Cviceni1412
{
class Student
{
private string jmeno; //Name
private string prijmeni; //Surname
private Vysvedceni vysvedceni;
//Dictionary<List<Student>, List<Vysvedceni>> vysvedceniTridy = new Dictionary<List<Student>, List<Vysvedceni>>();
List<Student> studenti = new List<Student>();
public void SetridStudenty()
{
var setrid = (from student in studenti select student)
.GroupBy(student => student.Jmeno.First()).ToList();
foreach (var item in setrid)
{
Console.WriteLine(item);
}
}
public override string ToString()
{
return "jmeno:"+jmeno + ",prijmeni:" + prijmeni;
}
public Student(string jmeno, string prijmeni, Vysvedceni vysvedceni)
{
this.jmeno = jmeno;
this.prijmeni = prijmeni;
this.vysvedceni = vysvedceni;
}
public string Jmeno { get => jmeno; set => jmeno = value; }
public string Prijmeni { get => prijmeni; set => prijmeni = value; }
internal Vysvedceni Vysvedceni { get => vysvedceni; set => vysvedceni = value; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Cviceni1412
{
class Vysvedceni
{
private Dictionary<Enum, List<int>> znamky = new Dictionary<Enum, List<int>>();
private Dictionary<Enum, int> vizo = new Dictionary<Enum, int>();
private Predmety skolniPredmety;
public Dictionary<Enum, List<int>> Znamky { get => znamky; set => znamky = value; }
internal Predmety SkolniPredmety { get => skolniPredmety; set => skolniPredmety = value; }
public Dictionary<Enum, int> Vizo{ get => vizo; set => vizo= value; }
public double Prumer(Enum predmet) //Method for calulating Average and final mark for student
{
double prumerZnamka=Znamky[predmet].Average();
return prumerZnamka ;
}
public void Vypis()//Print List method
{
Znamky.Select(i => $"{i.Key}:{i.Value}").ToList().ForEach(Console.WriteLine);
}
public Vysvedceni()
{
}
public enum Predmety//Subjects like math,Engl,P.E.,Physic
{
matematika,cestina,fyzika,programovani,telocvik,databaze,webovky
}
}
}
//Student class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Cviceni1412
{
class Student
{
private string jmeno;
private string prijmeni;
private Vysvedceni vysvedceni;
//Dictionary<List<Student>, List<Vysvedceni>> vysvedceniTridy = new Dictionary<List<Student>, List<Vysvedceni>>();
List<Student> studenti = new List<Student>();
public void SetridStudenty()//Sort students to the group by their first letter
{
var setrid = (from student in studenti select student)
.GroupBy(student => student.Jmeno.First()).ToList();
foreach (var item in setrid)
{
Console.WriteLine(item);
}
}
public override string ToString()
{
return "jmeno:"+jmeno + ",prijmeni:" + prijmeni;
}
public Student(string jmeno, string prijmeni, Vysvedceni vysvedceni)
{
this.jmeno = jmeno;
this.prijmeni = prijmeni;
this.vysvedceni = vysvedceni;
}
public string Jmeno { get => jmeno; set => jmeno = value; }
public string Prijmeni { get => prijmeni; set => prijmeni = value; }
internal Vysvedceni Vysvedceni { get => vysvedceni; set => vysvedceni = value; }
}
}
//Main class
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cviceni1412
{
class Program
{
static void Main(string[] args)
{
Student student = new Student("Jan","Novák",new Vysvedceni());
student.Vysvedceni.Znamky.Add(Vysvedceni.Predmety.matematika, new List<int>() {1,1,2,3,4,3 });//There is problem,List is declared and filled,but it prints just only Subject and [System.Generics...]
Console.WriteLine(student.Vysvedceni.Znamky.Keys.Count);
student.Vysvedceni.Vypis();
}
}
}
How I solved it.
Problem wasn't with ToString() method,but with Print() method.Linq iterated just Keys.Hope I helped
public void Print(Subjects sub)
{
foreach (var item in marks//dictionary)
{
Console.WriteLine(item.Key);
foreach (var ite in marks[sub])
{
Console.WriteLine(ite);
}
}
}
Below is an example payload response from an integration I am currently working on. The response does not set a default namespace (xml2 variable in example) and the issue is that XmlSerializer does not assume the default namespace is "d". I have tried setting the default namespace in the XMLSerializer constructor but that doesn't work either. As well, I can't expect the "service" to update/fix their side. Is there some other settings I can pass that will correctly populate the class?
Thanks,
Chuck
using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace TestXmlNamespace
{
[XmlRoot(ElementName = "root", Namespace = "my_test_ns1")]
public class Test
{
public string name { get; set; }
public int age { get; set; }
[XmlElement(Namespace = "my_test_ns2")]
public int ageInMonths { get; set; }
public override bool Equals(object obj)
{
return obj is Test b && name == b.name && age == b.age && ageInMonths == b.ageInMonths;
}
public void Run(string str, string name)
{
XmlSerializer serializer = new XmlSerializer(typeof(Test));
using (StringReader rStream = new StringReader(str))
{
Test test = serializer.Deserialize(rStream) as Test;
Console.Out.WriteLine(test.Equals(this) ? $"{name} equals expected" : $"{name} does not equal expected");
}
}
}
class Program
{
const string xml1 = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<d:name>Bill</d:name>
<d:age>32</d:age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
const string xml2 = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<name>Bill</name>
<age>32</age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
static void Main(string[] args)
{
Test expected = new Test()
{
name = "Bill",
age = 32,
ageInMonths = 384
};
expected.Run(xml1, "xml1");
expected.Run(xml2, "xml2");
}
}
}
I found the solution and I am posting it for those in the future who may run across this problem and hopefully I will help them save some time.
Simply put, when an element does not have a namespace prefix XmlSerializer treats it has not part of a name space so specifying an empty name space in the element attribute handles this. See the updated example code below.
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace TestXmlNamespace
{
public class CustomXmlTextReader : XmlTextReader
{
private readonly string DefaultNamespace = String.Empty;
public CustomXmlTextReader(StringReader reader, string defaultNamespace) : base(reader)
{
DefaultNamespace = defaultNamespace;
}
public override string NamespaceURI => String.IsNullOrEmpty(base.NamespaceURI) ? DefaultNamespace : base.NamespaceURI;
};
[XmlRoot(ElementName = "root", Namespace = "my_test_ns1")]
public class Test
{
[XmlElement("name", Namespace = "")] // Set namespace to empty string when no namespace prefix specified in xml
public string name { get; set; }
[XmlElement("age", Namespace = "")] // Set namespace to empty string when no namespace prefix specified in xml
public int age { get; set; }
[XmlElement(Namespace = "my_test_ns2")]
public int ageInMonths { get; set; }
public override bool Equals(object obj)
{
return obj is Test b && name == b.name && age == b.age && ageInMonths == b.ageInMonths;
}
public void Run(string str, string name)
{
using (StringReader rStream = new StringReader(str))
{
//using (XmlTextReader tr = new CustomXmlTextReader(rStream, "my_test_ns1"))
using (XmlTextReader tr = new XmlTextReader(rStream))
{
XmlSerializer serializer = new XmlSerializer(typeof(Test));
Test test = serializer.Deserialize(tr) as Test;
Console.Out.WriteLine(test.Equals(this) ? $"{name} equals expected" : $"{name} does not equal expected");
}
}
}
}
class Program
{
const string xml = #"<?xml version=""1.0"" encoding=""UTF-8"" ?>
<d:root xmlns:d=""my_test_ns1"" xmlns:v=""my_test_ns2"">
<name>Bill</name>
<age>32</age>
<v:ageInMonths>384</v:ageInMonths>
</d:root>
";
static void Main(string[] args)
{
Test expected = new Test()
{
name = "Bill",
age = 32,
ageInMonths = 384
};
expected.Run(xml, "xml1");
}
}
}
I have the following simple XML file and I am tying to read the attribute code, using a C# .NET Core Console. I really appetite for any help.
<course type="IT" date="19.09.2019">
<line code="IT001"/>
</course>
UPDATE
I need the result as an object.
Use xml serialization to get a class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Globalization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Course));
Course course = (Course)serializer.Deserialize(reader);
}
}
[XmlRoot("course")]
public class Course
{
[XmlAttribute("type")]
public string _type { get; set; }
public DateTime _date { get; set; }
[XmlAttribute("date")]
public string date {
get { return _date.ToString("dd.MM.yyyy"); }
set { _date = DateTime.ParseExact(value, "dd.MM.yyyy", System.Globalization.CultureInfo.InvariantCulture); }
}
private string _code { get; set; }
[XmlElement("line")]
public Line line
{
get { return new Line() { code = _code }; }
set { _code = value.code; }
}
}
[XmlRoot("line")]
public class Line
{
[XmlAttribute("code")]
public string code { get; set; }
}
}
Many ways to skin this cat. Here is a solution by making use of XPath
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml("<course type=\"IT\" date=\"19.09.2019\"> <line code=\"IT001\"/></course>");
var attrVal = xmlDoc.SelectSingleNode("/course/line/#code").Value;
Console.WriteLine("Attribute 'code' value is " + attrVal);
I am trying to deserialize an XML file within a C# program that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Addresses>
<ListName>Flowers</ListName>
<Address contextRef="RP.CC">Some Address</Address>
<Address contextRef="RP.BE">Some Other Address</Address>
<Address contextRef="RP.BV">Yet Another Address</Address>
<Address contextRef="RP.CAL">Wow, I Can't Believe It's Another Address</Address>
</Addresses>
I do not have any control over the format of this file. But, it will always have some combination of these 4 Address elements (i.e. these 4 contextRef attribute values are the only ones used) with differing element values each time.
Now, instead of deserializing into an Address array, I need to send them to individual properties within an Addresses object. My Current implementation uses an array and then a setter method to set these properties based on the contextRef as so:
public class Addresses
{
[XmlElement("ListName")]
public string ListName { get; set; }
private Address[] _addresses;
[XmlElement("Address")]
public Address[] AddressesArray
{
get
{
return _addresses;
}
set
{
_addresses = value;
SetAddress();
}
}
[XmlIgnore]
public Address AddressG21 { get; set; }
[XmlIgnore]
public Address AddressG22 { get; set; }
[XmlIgnore]
public Address AddressG23 { get; set; }
[XmlIgnore]
public Address AddressG9 { get; set; }
private void SetAddress()
{
foreach (var address in _addresses)
{
if (address.ContextRef == "RP.CC")
{
AddressG21 = address;
}
else if (address.ContextRef == "RP.BE")
{
AddressG22 = address;
}
else if (address.ContextRef == "RP.BV")
{
AddressG23 = address;
}
else if (address.ContextRef == "RP.CAL")
{
AddressG9 = address;
}
}
}
}
Where the Address object is defined as so:
public class Address
{
private string valueField;
/// <remarks/>
[XmlText]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
[XmlAttribute("contextRef")]
public string ContextRef { get; set; }
}
So, my question is, is there a neater/better way of deserializing this XML directly into the AddressG21, etc. object properties without first using the Address array?
Thanks in advance.
I would use xml linq and create a dictionary in the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Addresses addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
If you had multiple Addresses elements then use this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Addresses> addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
How correctly to deserialize XML from my example (look the question in the end)? Am I doing this right? Maybe there is a way to make it easier and more efficient?
XML:
<Warehouse>
<GUID>0d63057d-99e8-11e6-813b-0003ff000011</GUID>
<Name>WarehouseName</Name>
<Terms>
<Term TargetGUID="490ecabf-f011-11e3-b7d9-6c626dc1e098">2</Term>
<Term TargetGUID="f332d7ff-efd2-11e3-b7d9-6c626dc1e098">4</Term>
</Terms>
</Warehouse>
C#:
Warehouse.cs:
[Serializable]
public class Warehouse
{
[XmlArray("Terms", IsNullable=true)]
[XmlArrayItem("Term")]
public WarehouseTransferTerm[] TransferTerms { get; set; }
[XmlElement(ElementName="Name")]
public string InternalName { get; set; }
[XmlElement(ElementName="Guid")]
public Guid Guid { get; set; }
}
WarehouseTransferTerm.cs:
[Serializable]
public class WarehouseTransferTerm
{
public Guid SourceWarehouseGuid { get; set; }
[XmlAttribute(AttributeName = "TargetGUID")]
public Guid TargetWarehouseGuid { get; set; }
[XmlElement(ElementName="Term")]
public int TransferTermInDays { get; set; }
}
Question: How I can set Warehouse's GUID property value as SourceWarehouseGuid?
You could implement custom deserialization logic and just set the value on the dependent items. See here: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.ondeserializedattribute?view=netframework-4.7.2
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
foreach(var term in TransferTerms)
{
term.TargetWarehouseGuid = this.Guid;
}
}
I like using Dictionaries along with Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, XElement> warehouses = doc.Descendants("Warehouse")
.GroupBy(x => (string)x.Element("GUID"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
XElement warehouse = warehouses["0d63057d-99e8-11e6-813b-0003ff000011"];
Dictionary<string, XElement> terms = warehouse.Descendants("Term")
.GroupBy(x => (string)x.Attribute("TargetGUID"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
string value = terms["490ecabf-f011-11e3-b7d9-6c626dc1e098"].Value;
warehouse.SetValue(value);
}
}
}