c# XmlElment change order in derived class - c#

I'm trying to serialize a XML. I want to add a new property in a derived class and place it between the other properties of the base class.
In the sample I want to place the 'SpecialInterests' between the 'Name' and the 'Age' of the base class.
XML
<!-- Output -->
<PersonenListe xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersonenArray>
<PersonObjekt>
<Name>Max Man</Name>
<Age>33</Age>
</PersonObjekt>
<PersonObjekt xsi:type="SpecialPerson">
<Name>Albert Einstein</Name>
<Age>36</Age>
<SpecialInterests>Physics</SpecialInterests>
</PersonObjekt>
</PersonenArray>
</PersonenListe>
<!-- Desire -->
<PersonenListe>
<PersonenArray>
<PersonObjekt>
<Name>Max Man</Name>
<Age>33</Age>
</PersonObjekt>
<PersonObjekt xsi:type="SpecialPerson">
<Name>Albert Einstein</Name>
<SpecialInterests>Physics</SpecialInterests>
<Age>36</Age>
</PersonObjekt>
</PersonenArray>
</PersonenListe>
c# - Main (Creating instances and serialization)
static void Main(string[] args)
{
PersonalList personen = new PersonalList();
Person normPerson = new Person { Name = "Max Man", Age = 33 };
SpecialPerson specPerson = new SpecialPerson
{ Name = "Albert Einstein", Interests = "Physics", Age = 36 };
personen.AddPerson(normPerson);
personen.AddPerson(specPerson);
Type[] personTypes = new Type[personen.Persons.Count];
for (int i = 0; i < personen.Persons.Count; i++)
{
personTypes[i] = personen.Persons[i].GetType();
}
XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes);
FileStream fs = new FileStream("Personenliste.xml", FileMode.Create);
serializer.Serialize(fs, personen);
fs.Close();
personen = null;
// Deserialize
fs = new FileStream("Personenliste.xml", FileMode.Open);
personen = (PersonalList)serializer.Deserialize(fs);
serializer.Serialize(Console.Out, personen);
Console.ReadLine();
}
Models
[XmlRoot("PersonenListe")]
[XmlInclude(typeof(Person))]
public class PersonalList
{
[XmlArray("PersonenArray")]
[XmlArrayItem("PersonObjekt")]
public List<Person> Persons = new List<Person>();
public void AddPerson(Person person)
{
Persons.Add(person);
}
}
[XmlType("Person")]
public class Person
{
[XmlElement(ElementName = "Name", Order = 1)]
public virtual string Name { get; set; }
[XmlElement(ElementName = "Age", Order = 3)]
public virtual int Age { get; set; }
}
[XmlType("SpecialPerson")]
public class SpecialPerson : Person
{
[XmlElement(ElementName = "Name", Order = 1)]
public override string Name { get; set; }
[XmlElement(ElementName = "SpecialInterests", Order = 2)]
public string Interests { get; set; }
[XmlElement(ElementName = "Age", Order = 3)]
public override int Age { get; set; }
}

This question is essentially the same this other question. There is a good explanation of why this serialization behavior is the way it is and a few approaches for dealing with it.
.NET Serialization Ordering

Related

Circular Reference in XmlSerializer c#

I'm making an application where I would connect signals with eachother. This creates a circular reference.
The examples I've seen show solutions for a Parent-Child structure. In my application I connect 2 objects from the same class with eachother, so I won't be able to simply ignore one of the references.
I've made a quick example of what my app is doing:
class Program
{
static void Main(string[] args)
{
Info i = new Info();
Employee bs = new Employee("asfdljfoiej", i);
Employee ss = new Employee("asfdljfj", i);
bs.conEm = ss;
ss.conEm = bs;
XmlSerializer xs = new XmlSerializer(typeof(Employee));
const string path = #"C:\Users\Joris.Bosma.KG\source\repos\TestProject\TestProject\bin\Debug\Serializing.xml";
TextWriter txtWriter = new StreamWriter(path);
xs.Serialize(txtWriter, bs);
txtWriter.Close();
//---------------------------------------------------------------------------------------------------------------------------------------
Program p = new Program();
p.Deserialize("Serializing.xml");
}
public void Deserialize(string filename)
{
XmlSerializer xs2 = new XmlSerializer(typeof(Employee));
Employee em;
using (Stream reader = new FileStream(filename, FileMode.Open))
em = (Employee)xs2.Deserialize(reader);
Console.Write(em.name);
}
}
public class Employee
{
public int Id = 1;
public String name = "John Smith";
public string subject = "Physics";
public string random;
public List<Employee> Employees;
public Employee conEm;
public Info inf;
public Employee()
{
}
public Employee(String s, Info i)
{
random = s;
inf = i;
}
}
public class Info : Employee
{
public string add = "Street";
}
The problem lays in
bs.conEm = ss
ss.conEm = bs
Thanks for helping in advance!
If structure of employees is not hierarchial (loops are possible), I would recommend to exclude properties 'conEm' and 'Employees' from serialization and store data in structure like this:
public class Relationship {
public int ParentId { get; set; }
public int ChildId { get; set; }
}
public class DataStore {
// flat list of employees
public List<Employee> Employees { get; set; }
// list of all relationship between Employees
public List<Relationship> Relationships { get; set; }
}
This structure needs to be populated before serialization.
After deserialization, structure can easily be restored:
var index = data.Employees.ToLookup(e => e.Id);
data.Relationships.Iter(r => {
var parent = index[r.ParentId].FirstOrDefault();
var child = index[r.ChildId].FirstOrDefault();
if (parent != nuu && child != null) {
parent.Employees.Add(child);
child.conEm = parent;
}
});

Deserialising XML with different element name in c#

XML:
<?xml version="1.0" encoding="UTF-8"?>
<Images>
<I0>
<Path>123.com</Path>
<I0>
<I1>
<Path>123.com</Path>
<I1>
<I2>
<Path>123.com</Path>
<I2>
</Images>
Can serializer.Deserialize() be used to get tags with different names into a collection?
currently, in my object I have:
C#:
public class rootObject
{
[XmlElement(ElementName = "I0")]
public I0 I0 { get; set; }
[XmlElement(ElementName = "I1")]
public I1 I1 { get; set; }
[XmlElement(ElementName = "I2")]
public I2 I2 { get; set; }
}
But I would like to have (Because Images can have more or fewer elements):
public class rootObject
{
public List<I> Is { get; set; }
}
You can do what you are suggesting you just merely need to pass in the type argument in your class doing the generic. The key point to remember when you do a deserialization routine is that the routine needs to know the sub reference. So if I was to say string.Deserialize it would bomb. It would need to know a reference string.Deserialize> where Sub could be the class object that may change.
Say I have a base class and I want 'T' to be a type I can change for extensible abilities later.
[Serializable]
public class Test<T> where T : class
{
public Test() { }
public int TestId { get; set; }
public string Name { get; set; }
public List<T> Shipments { get; set; }
}
I want to test this with two classes I just make up that have different properties slightly
[Serializable]
public class Sub1
{
public int Id { get; set; }
public string Desc { get; set; }
}
[Serializable]
public class Sub2
{
public int IdWhatever { get; set; }
public string DescWhatever { get; set; }
}
Now let's do a main program and test serialization.
class Program
{
static void Main(string[] args)
{
var serializeTest = new Test<Sub1> { TestId = 1, Name = "Test", Shipments = new List<Sub1> { new Sub1 { Id = 1, Desc = "Test" }, new Sub1 { Id = 2, Desc = "Test2" } } };
var serializeTest2 = new Test<Sub2> { TestId = 1, Name = "Test", Shipments = new List<Sub2> { new Sub2 { IdWhatever = 1, DescWhatever = "Test" }, new Sub2 { IdWhatever = 2, DescWhatever = "Test2" } } };
var serialized = serializeTest.SerializeToXml();
var serialized2 = serializeTest2.SerializeToXml();
var deserialized = serialized.DeserializeXml<Test<Sub1>>();
var deserialized2 = serialized2.DeserializeXml<Test<Sub2>>();
Console.WriteLine(serialized);
Console.WriteLine();
Console.WriteLine(serialized2);
Console.ReadLine();
}
}
And my Serialize and DeSerialize extension methods:
public static string SerializeToXml<T>(this T valueToSerialize, string namespaceUsed = null)
{
var ns = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, (namespaceUsed != null) ? namespaceUsed : string.Empty) });
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(typeof(T));
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
You don't need to specify the XmlElement name when the properties match the XML. A few solutions, some kinda hacky :).
HACKY: use regex string replace to replace <I#> and </I#> to
just <I> and </I>
SOMEWHAT HACKY: This might work for you:
How to deserialize an XML array containing multiple types of elements in C#,
but you'd have to add an attribute for i0, i1 ... i100, etc.
BEST: Is that your entire XML? I'd honestly just use LINQToXml and
do a Descendants("Path") and get an array of strings back with 1 line of code. Serialization is not really the best solution for this.

cant figure out how to map this json into C# classes

So I have the json below that I want to Deseralize into Classes so I can work with it. But the issues is that the top two fields are a different type to all the rest
"items": {
"averageItemLevel": 718,
"averageItemLevelEquipped": 716,
"head": { ... },
"chest": { ... },
"feet": { ... },
"hands": { ... }
}
Where ... is a the Item class below, but the problem is that 2 of the fields are ints and the rest are Item, there are about 20 fields in total. So what I'd like to do is put them into a Dictionary<string, Item> but the 2 int fields are preventing me from Deseralizing it into that. I'm using JavaScriptSerializer.Deserialize<T>() to do this.
I could have each item as it's own class with the name of the item as the name of the class, but I find that to be very bad, repeating so much each time, also very hard to work with later since I cant iterate over the fields, where as I could a Dictionary. Any idea how I could overcome this?
public class Item
{
public ItemDetails itemDetails { get; set; }
public int id { get; set; }
public string name { get; set; }
public string icon { get; set; }
public int quality { get; set; }
public int itemLevel { get; set; }
public TooltipParams tooltipParams { get; set; }
public List<Stat> stats { get; set; }
public int armor { get; set; }
public string context { get; set; }
public List<int> bonusLists { get; set; }
}
Update: from the comments I came up with this solution
JObject jsonObject = JObject.Parse(json);
jsonObject["averageItemLevel"] = int.Parse(jsonObject["items"]["averageItemLevel"].ToString());
jsonObject["averageItemLevelEquipped"] = int.Parse(jsonObject["items"]["averageItemLevelEquipped"].ToString());
jsonObject["items"]["averageItemLevel"].Parent.Remove();
jsonObject["items"]["averageItemLevelEquipped"].Parent.Remove();
var finalJson = jsonObject.ToString(Newtonsoft.Json.Formatting.None);
var character = _serializer.Deserialize<Character>(finalJson);
character.progression.raids.RemoveAll(x => x.name != "My House");
return character
If I add these two classes to match your JSON I can serialize and deserialize the objects:
public class root
{
public Items items { get; set; }
}
public class Items
{
public int averageItemLevel { get; set; }
public int averageItemLevelEquipped { get; set; }
public Item head {get;set;}
public Item chest {get;set;}
public Item feet {get;set;}
public Item hands {get;set;}
}
Test rig with the WCF Serializer:
var obj = new root();
obj.items = new Items
{
averageItemLevel = 42,
feet = new Item { armor = 4242 },
chest = new Item { name = "super chest" }
};
var ser = new DataContractJsonSerializer(typeof(root));
using (var ms = new MemoryStream())
{
ser.WriteObject(ms, obj);
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
Console.WriteLine("and deserialize");
ms.Position = 0;
var deserializeObject = (root) ser.ReadObject(ms);
Console.WriteLine(deserializeObject.items.feet.armor);
}
And with the JavaScriptSerializer:
var jsser = new JavaScriptSerializer();
var json = jsser.Serialize(obj);
Console.WriteLine(json);
Console.WriteLine("and deserialize");
var djson = jsser.Deserialize<root>(json);
Console.WriteLine(djson.items.feet.armor);
Both serializers give the same result for your given JSON.

Serialize only those properties which are specified [XmlElement] without changing the original class

Code:
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
}
Now I want to serilize only thoese which are specified with [XmlElement], companyid not to be serilized.
So, what can I do?
Here's a simple example I put together in LinqPad. The first 4 lines of the Main method set up an XmlAttributeOverrides instance that is then used to tell the XmlSerializer to not serialize the companyid property.
void Main()
{
//Serialize, but ignore companyid
var overrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes();
attributes.XmlIgnore = true;
overrides.Add(typeof(MyClass), "companyid", attributes);
using(var sw = new StringWriter()) {
var xs = new XmlSerializer(typeof(MyClass), overrides);
var a = new MyClass() {
Company = "Company Name",
Amount = 10M,
companyid = 7
};
xs.Serialize(sw, a);
Console.WriteLine(sw.ToString());
}
}
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
}
The output of this program is:
<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Company>Company Name</Company>
<Amount>10</Amount>
</MyClass>
If you need this code to inspect the class to determine which properties to exclude based on an XmlElementAttribute not being present, then you can modify the above code to use reflection to enumerate the properties of the object. For every property that does not have an XmlElementAttribute, add an item to the overrides instance.
For example:
void Main()
{
//Serialize, but ignore properties that do not have XmlElementAttribute
var overrides = new XmlAttributeOverrides();
var attributes = new XmlAttributes();
attributes.XmlIgnore = true;
foreach(var prop in typeof(MyClass).GetProperties())
{
var attrs = prop.GetCustomAttributes(typeof(XmlElementAttribute));
if(attrs.Count() == 0)
overrides.Add(prop.DeclaringType, prop.Name, attributes);
}
using(var sw = new StringWriter()) {
var xs = new XmlSerializer(typeof(MyClass), overrides);
var a = new MyClass() {
Company = "Company Name",
Amount = 10M,
companyid = 7,
blah = "123" };
xs.Serialize(sw, a);
Console.WriteLine(sw.ToString());
}
}
[Serializable]
public class MyClass
{
[XmlElement("Company")]
public string Company { get; set; }
[XmlElement("Amount")]
public decimal Amount { get; set; }
public int companyid { get; set; }
public string blah { get; set; }
}

Map entity to JSON using JavaScriptSerializer

My entities are like this:
class Address
{
public string Number { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Address PostalAddress { get; set; }
}
Person newPerson =
new Person()
{
Name = "Kushan",
Age = 25,
PostalAddress =
new Address()
{
Number = "No 25",
Street = "Main Street",
City = "Matale",
Country = "Sri Lanka"
}
};
Now I wanna map this newPerson object into JSON object like this,
{
"PER_NAME" : "Kushan",
"PER_AGE" : "25",
"PER_ADDRESS" : {
"ADD_NUMBER" : "No 25",
"ADD_STREET" : "Main Street",
"ADD_CITY" : "Matale",
"ADD_COUNTRY" : "Sri Lanka"
}
}
Note: Above is just an example.
What I need is, I need to customize the Key at the serializing time. by default it is taking property name as the key. I can't change property names. How to do this?
Also, is it possible to change to order of appearing key-value pairs in JSON obj.?
You need to add DataContract attributes to your classes and DataMember to the properties. Set Name property of DataMemeber attribute to your custom property name and Order property to define the order.
[DataContract]
public class Person
{
[DataMember(Name = "PER_NAME", Order = 1)]
public string Name { get; set; }
[DataMember(Name = "PER_AGE", Order = 2)]
public int Age { get; set; }
[DataMember(Name = "PER_ADDRESS", Order = 3)]
public Address PostalAddress { get; set; }
}
Then you can do this:
var newPerson = new Person()
{
Name = "Kushan",
Age = 25,
PostalAddress = new Address()
{
Number = "No 25",
Street = "Main Street",
City = "Matale",
Country = "Sri Lanka"
}
};
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));
ser.WriteObject(stream, newPerson);
To check the result:
var result = Encoding.ASCII.GetString(stream.ToArray());
{"PER_NAME":"Kushan","PER_AGE":25,"PER_ADDRESS":{"ADD_NUMBER":"No 25","ADD_STREET":"Main Street","ADD_CITY":"Matale","ADD_COUNTRY":"Sri Lanka"}}
You can serialize an anonymous type with JavaScriptSerializer, so you might try projecting your object into the shape you want to serialize:
person.Select(s => new { PER_NAME = s.Name, PER_AGE = s.Age });

Categories

Resources