I am creating an app that fetches fuel prices from a web service. I want to deserialise the prices a IDictionary of Price objects with the fuel as the key (similar to this).
I had created a setter to do this but have since found out that the serialisation uses the Add method rather than the setter for lists. Is there a way of doing this using the serialisation API or will I have to write custom serialisation code?
The XML looks like this
<?xml version="1.0"?>
<prices>
<price fuel="Petrol">152.43</price>
<price fuel="Diesel">147.53</price>
</prices>
The code looks like this
[XmlRoot("prices")]
public class FuelPrices
{
private IDictionary<Fuel, Price> prices = new Dictionary<Fuel, Price>();
// This is used for serialising to XML
[XmlElement("price")]
public ICollection<Price> Prices
{
get
{
return prices.Values;
}
set
{
prices = new Dictionary<Fuel, Price>();
foreach (Price price in value)
{
prices[price.Fuel] = price;
}
}
}
// These properties are used to access the prices in the code
[XmlIgnore]
public Price PetrolPrice
{
get
{
Price petrolPrice;
prices.TryGetValue(Fuel.Petrol, out petrolPrice);
return petrolPrice;
}
}
[XmlIgnore]
public Price DieselPrice
{
get
{
Price dieselPrice;
prices.TryGetValue(Fuel.Diesel, out dieselPrice);
return dieselPrice;
}
}
}
You can write a wrapper around a dictionary along the lines of
sealed class DictionaryWrapper<K, T> : ICollection<T>
{
private readonly Func<T, K> m_keyProjection ;
private readonly IDictionary<K, T> m_dictionary ;
// expose the wrapped dictionary
public IDictionary<K, T> Dictionary { get { return m_dictionary ; }}
public void Add (T value)
{
m_dictionary[m_keyProjection (value)] = value ;
}
public IEnumerator<T> GetEnumerator ()
{
return m_dictionary.Values.GetEnumerator () ;
}
// the rest is left as excercise for the reader
}
and use it like this
private DictionaryWrapper<Fuel, Price> pricesWrapper =
new DictionaryWrapper<Fuel, Price> (
new Dictionary<Fuel, Price> (), price => price.Fuel) ;
[XmlElement("price")]
public ICollection<Price> Prices
{
get { return pricesWrapper ; } // NB: no setter is necessary
}
if you do not want to write custom serialization - you can do this:
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
public class Program
{
[XmlType("price")]
public class Price
{
[XmlText]
public double price { get; set; }
[XmlAttribute("fuel")]
public string fuel { get; set; }
}
[XmlType("prices")]
public class PriceList : List<Price>
{
}
static void Main(string[] args)
{
//Serialize
var plist = new PriceList()
{
new Price {price = 153.9, fuel = "Diesel"},
new Price {price = 120.6, fuel = "Petrol"}
};
var serializer = new XmlSerializer(typeof(PriceList));
var sw = new StringWriter();
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(sw, plist, ns);
var result = sw.ToString();//result xml as we like
//Deserialize
var sr = new StringReader(result);
var templist = (PriceList)serializer.Deserialize(sr);
var myDictionary = templist.ToDictionary(item => item.fuel, item => item.price);
}
}
}
and if you need custom serialization - look at this post:Why isn't there an XML-serializable dictionary in .NET?
Related
Running this program..
using System;
using System.Text.Json;
using System.Collections.Generic;
namespace XmlConsoleApp
{
class Program
{
static void Main(string[] args)
{
CampaignAccountMap campaignAccountMap = new CampaignAccountMap();
CampaignAccountMapItem campaignAccountMapItem = new CampaignAccountMapItem();
campaignAccountMapItem.CampaignId = #"dfdf-fdfdd-fdfd-dfdfd-dfdfd";
campaignAccountMapItem.AccountIds.Add(1245345);
campaignAccountMapItem.AccountIds.Add(1345675);
campaignAccountMap.Campaigns.Add(campaignAccountMapItem);
string jsonString = JsonSerializer.Serialize<CampaignAccountMap>(campaignAccountMap);
Console.Write(jsonString);
}
}
}
class CampaignAccountMap
{
public List<CampaignAccountMapItem> Campaigns { get; set; }
public CampaignAccountMap()
{
Campaigns = new List<CampaignAccountMapItem>();
}
}
class CampaignAccountMapItem
{
public string CampaignId { get; set; }
public List<int> AccountIds { get; set; }
public CampaignAccountMapItem()
{
AccountIds = new List<int>();
}
}
Outputs this JSON:
{
"Campaigns": [
{
"CampaignId": "dfdf-fdfdd-fdfd-dfdfd-dfdfd",
"AccountIds": [
1245345,
1345675
]
}
]
}
How can I customize the serialized output such that it produces this output instead?
{
"dfdf-fdfdd-fdfd-dfdfd-dfdfd": [
1245345,
1345675
]
}
Changing the name of the properties in from the class to the JSON seems doable using sterilizing annotations and such but I cant find any examples where those names are dynamic, ie I need the keys in the JSON output to be the dynamic values from the CampaignId property in the class.
I'm also open to doing this without using classes if I need to.
One way is to just use a Dictionary.
var dict = new Dictionary<string, List<int>>();
foreach (var item in campaignAccountMap.Campaigns)
{
dict[item.CampaignId] = item.AccountIds;
}
var jsonString = JsonSerializer.Serialize(dict);
Console.Write(jsonString);
The desired json format can be represented by Dictionary<string, List<int>>.
So, you could add a ToDictionary method to your class, something like this:
class CampaignAccountMapItem
{
public string CampaignId { get; set; }
public List<int> AccountIds { get; set; }
public CampaignAccountMapItem()
{
AccountIds = new List<int>();
}
public Dictionary<string, List<int>> ToDictionary()
{
return new Dictionary<string, List<int>> {{ CampaignId, AccountIds }};
}
}
And use it like:
string jsonString = JsonSerializer.Serialize(campaignAccountMapItem.ToDictionary());
Console.Write(jsonString);
I have a class with fields - Id , name, company and another 29 fields.
public class Employee
{
[XmlAttribute("textbox1")]
public int Id { get; set; }
[XmlAttribute("textbox11")]
public string Name { get; set; }
[XmlAttribute("textbox21")]
public string Company { get; set; }
[XmlAttribute("textbox22")]
public bool Val1 { get; set; }
[XmlAttribute("textbox23")]
public bool Val2 { get; set; }
public bool TestSomething{get;set;}
}
public class ParseData()
{
List<Employee> employee = new List<Employee>();
XmlSerialiser serializer = new XmlSerializer();
using(FileStream stream = new FileStream("test.xml", FileMode.Open))
{
employees = (Employee)serializer.Deserialize(stream);
}
//Todo: Add the new calculated value TestSomething
}
I'm deserialising the values from the xml data ( which has around 100 employees).
Now I have a new field 'TestSomething' which is not deserialised but will be result of deserialised value as shown below. Is there a way I could add calculated values to the existing list employees ?
foreach(var i in employees)
{
employee.TestSomething = (Val1 == true) ? Val1 : Val2;
}
Any suggestion on this will be appreciated.
The code snippets below are extension methods I use to serialize and deserialize xml strings. Note these need to be put in a static class and referenced in the class for use.
/// <summary>
/// Serializes the object to xml string.
/// </summary>
/// <returns>xml serialization.</returns>
public static string SerializeToXml<T>(this T instance)
where T : class, new()
{
string result;
var format = new XmlSerializer(typeof(T));
using (var strmr = new StringWriter())
{
format.Serialize(strmr, instance);
result = strmr.ToString();
}
return result;
}
/// <summary>
/// Deserializes xml serialized objects.
/// </summary>
/// <param name="xmlDocument">serialized string.</param>
/// <param name="result">out parameter.</param>
/// <returns>Instance object.</returns>
public static bool TryParseXml<T>(this string xmlDocument, out T result)
where T : class, new()
{
result = null;
if (string.IsNullOrWhiteSpace(xmlDocument))
{
return false;
}
try
{
using (TextReader str = new StringReader(xmlDocument))
{
var format = new XmlSerializer(typeof(T));
XmlReader xmlReader = XmlReader.Create(str);
if (format.CanDeserialize(xmlReader))
{
result = format.Deserialize(xmlReader) as T;
}
}
}
catch (InvalidOperationException)
{
return false;
}
return !(result is null);
}
In practice you would have a class of some type that has properties that you would like to store to a disk or database etc. Lets say I have a class:
public class MyPoint
{
public double X { get; set;}
public double Y { get; set;}
}
If I wanted to store a list of MyPoint without much heavy lifting I could serialize the class directly using the SerializeToXml method above.
var myPoints = new List<MyPoint>{new MyPoint{ X= 1, Y = 2}, new MyPoint{X = 3, Y = 4}};
var xmlString = myPoints.SerializeToXml();
Everything is fine and dandy, my info is saved as xml in some file somewhere. But then my boss says, "Dang, sorry about this but we need a Z value now that is computed from the X and Y of each using Z = X * X + Y * Y."
Adding properties to the serialized class isn't a problem (taking properties out is!!
The serializer will try to assign a value to a property that doesn't exist and throw an exception.). But in this case we don't need to worry. I go to the original MyPoint class and add a Z.
public class MyPoint
{
public double X { get; set;}
public double Y { get; set;}
public double Z { get; set;} // <---- Here
}
Now when I parse the xml I will get classes with the new property. Using the TryParseXml I get my list of points.
// assume I have a string already from the xml document to use.
List<MyPoint> points = null;
if(serializedPoints.TryParse(out points))
{
foreach(var point in points)
{
point.Z = X * X + Y * Y;
}
}
var serializedPointsWithZ = points.SerializeToXml();
Now serializedPointsWithZ is just a string that can be written to a file etc.
I would like to deserialize an XML File to a class with several subclasses. The XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Objects>
<Group index="1">
<de>
<GroupName>ANTRIEB</GroupName>
</de>
<en>
<GroupName>missing translation!</GroupName>
</en>
<Level>2</Level>
</Group>
<Group index="2">
<de>
<GroupName>BREMSEN</GroupName>
</de>
<Level>3</Level>
</Group>
</Objects>
Deserializing the XML to classes would be no problem, if there wouldn't be those language tags. Sure, I could create a property for every language tag possible. But the list of languages possible should be dynamic (e.g. read from an config file).
This is the reason why i would like to deserialize those language tags and their content into a Dictionary which uses the language as key and a model for the content.
My models look like this:
[XmlRoot("Objects")]
public class DeactivationsXml
{
[XmlElement("Group")]
public DeactivationsGroup[] Groups { get; set; }
}
[Serializable()]
public class DeactivationsGroup
{
[XmlIgnore]
public Dictionary<string, GroupName> GroupNames { get; set; } = new Dictionary<string, GroupName>();
public int Level { get; set; }
[XmlAttribute]
public byte index { get; set; }
}
public class GroupName
{
[XmlElement("GroupName")]
public string Name { get; set; }
}
I searched for a long time to address this problem, but couldn't find a solution. I'm pretty sure, that it's not possible to solve this Problem just with attributes.
Does some hybrid aproach exist in order to combine the Deserialization of an XML File in combination with manual deserialization of all XmlElements which could not be automatically deserialized?
A good and extensible solution for my problem would be great, because the XML structure is complex (same Problem several times with different content etc.).
I can't change the structure of the XML, so please don't point this out.
Approaches
IXmlSerializable
I tried to implement the IXmlSerializable Interface on the DeactivationsGroup class in order to search with a list of given languages for XmlElements with those names and deserialize the content of those XmlElements.
But this approach didn't work out, because you have to map all properties manually.
IExtensibleDataObject
The Interface is only supported by a DataContractSerializer. In the worst case i could use this interface to deserialize after Deserializing, if no other solution is found..
OnDeserialization
This Attribute is not supported by XmlSerializer, but would provide the functionality i possibly need.
XmlAnyElement
I guess this is the best option at this point. Does some callback exist after deserialization finished in order to automate this?
Executable Code
Here's the whole code so far.
public void Parse()
{
string xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
" <Objects>" +
" <Group index=\"1\">" +
" <de>" +
" <GroupName>ANTRIEB</GroupName>" +
" </de>" +
" <en>" +
" <GroupName>missing translation!</GroupName>" +
" </en>" +
" <Level>2</Level>" +
" </Group>" +
" <Group index=\"2\">" +
" <de>" +
" <GroupName>BREMSEN</GroupName>" +
" </de>" +
" <Level>3</Level>" +
" </Group>" +
" </Objects>";
XmlSerializer serializer = new XmlSerializer(typeof(DeactivationsXml));
using (TextReader fileStream = new StringReader(xml))
{
var result = (DeactivationsXml)serializer.Deserialize(fileStream);
}
}
[XmlRoot("Objects")]
public class DeactivationsXml
{
[XmlElement("Group")]
public DeactivationsGroup[] Groups { get; set; }
}
[Serializable()]
public class DeactivationsGroup
{
[XmlIgnore]
public Dictionary<string, GroupName> GroupNames { get; set; } = new Dictionary<string, GroupName>();
public int Level { get; set; }
[XmlAttribute]
public byte index { get; set; }
}
public class GroupName
{
[XmlElement("GroupName")]
public string Name { get; set; }
}
You can adopt the approach from this answer and add a surrogate XmlElement [] property, marked with [XmlAnyElement], that performs a nested (de)serialization on the key/value pairs of the Dictionary<string, GroupName> property, binding the dictionary keys to the element names.
Note that, while the documentation for XmlAnyElementAttribute states
Specifies that the member (a field that returns an array of XmlElement or XmlNode objects) contains objects that represent any XML element that has no corresponding member in the object being serialized or deserialized.
In fact the attribute can be applied to a property as well. Thus a (de)serialization callback is not required since the nested serialization can be performed inside the getter and setter for the surrogate property itself. It can also be applied to members returning an array of XElement objects instead of XmlElement if you prefer the new LINQ-to-XML API.
In this approach, your DeactivationsGroup would look like:
[Serializable()]
public class DeactivationsGroup
{
public DeactivationsGroup() { this.GroupNames = new Dictionary<string, GroupName>(); }
[XmlIgnore]
public Dictionary<string, GroupName> GroupNames { get; set; }
public int Level { get; set; }
[XmlAttribute]
public byte index { get; set; }
[XmlAnyElement]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public XElement[] XmlGroupNames
{
get
{
return GroupNames.SerializeToXElements(null);
}
set
{
if (value == null || value.Length < 1)
return;
foreach (var pair in value.DeserializeFromXElements<GroupName>())
{
GroupNames.Add(pair.Key, pair.Value);
}
}
}
}
Making use of the following extension methods and classes:
public static class XmlKeyValueListHelper
{
const string RootLocalName = "Root";
public static XElement [] SerializeToXElements<T>(this IEnumerable<KeyValuePair<string, T>> dictionary, XNamespace ns)
{
if (dictionary == null)
return null;
ns = ns ?? "";
var serializer = XmlSerializerFactory.Create(typeof(T), RootLocalName, ns.NamespaceName);
var array = dictionary
.Select(p => new { p.Key, Value = p.Value.SerializeToXElement(serializer, true) })
// Fix name and remove redundant xmlns= attributes. XmlWriter will add them back if needed.
.Select(p => new XElement(ns + p.Key, p.Value.Attributes().Where(a => !a.IsNamespaceDeclaration), p.Value.Elements()))
.ToArray();
return array;
}
public static IEnumerable<KeyValuePair<string, T>> DeserializeFromXElements<T>(this IEnumerable<XElement> elements)
{
if (elements == null)
yield break;
XmlSerializer serializer = null;
XNamespace ns = null;
foreach (var element in elements)
{
if (serializer == null || element.Name.Namespace != ns)
{
ns = element.Name.Namespace;
serializer = XmlSerializerFactory.Create(typeof(T), RootLocalName, ns.NamespaceName);
}
var elementToDeserialize = new XElement(ns + RootLocalName, element.Attributes(), element.Elements());
yield return new KeyValuePair<string, T>(element.Name.LocalName, elementToDeserialize.Deserialize<T>(serializer));
}
}
public static XmlSerializerNamespaces NoStandardXmlNamespaces()
{
var ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
return ns;
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(null, NoStandardXmlNamespaces());
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializerNamespaces ns)
{
return obj.SerializeToXElement(null, ns);
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
return obj.SerializeToXElement(serializer, (omitStandardNamespaces ? NoStandardXmlNamespaces() : null));
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, XmlSerializerNamespaces ns)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(writer, obj, ns);
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
return (T)result;
}
}
}
public static class XmlSerializerFactory
{
// To avoid a memory leak the serializer must be cached.
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
// This factory taken from
// https://stackoverflow.com/questions/34128757/wrap-properties-with-cdata-section-xml-serialization-c-sharp/34138648#34138648
readonly static Dictionary<Tuple<Type, string, string>, XmlSerializer> cache;
readonly static object padlock;
static XmlSerializerFactory()
{
padlock = new object();
cache = new Dictionary<Tuple<Type, string, string>, XmlSerializer>();
}
public static XmlSerializer Create(Type serializedType, string rootName, string rootNamespace)
{
if (serializedType == null)
throw new ArgumentNullException();
if (rootName == null && rootNamespace == null)
return new XmlSerializer(serializedType);
lock (padlock)
{
XmlSerializer serializer;
var key = Tuple.Create(serializedType, rootName, rootNamespace);
if (!cache.TryGetValue(key, out serializer))
cache[key] = serializer = new XmlSerializer(serializedType, new XmlRootAttribute { ElementName = rootName, Namespace = rootNamespace });
return serializer;
}
}
}
Sample fiddle. And another demonstrating a case with XML namespaces and attributes.
Try following using 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);
DeactivationsGroup.GroupNames = doc.Descendants("Group").Select(x => new {
languages = x.Elements().Where(y => y.Element("GroupName") != null).Select(y => new DeactivationsGroup() {
name = (string)y.Element("GroupName"),
level = (int)x.Element("Level"),
index = byte.Parse((string)x.Attribute("index")),
language = y.Name.LocalName
})
}).SelectMany(y => y.languages)
.GroupBy(x => x.name, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
public class DeactivationsGroup
{
public static Dictionary<string, DeactivationsGroup> GroupNames { get; set; }
public string name { get; set; }
public int level { get; set; }
public byte index { get; set; }
public string language { get; set; }
}
}
}
This is easily done as follows.
Use your set of classes.
Set event handler on the serializer:
var serializer = new XmlSerializer(typeof(DeactivationsXml));
serializer.UnknownElement += Serializer_UnknownElement;
The code in this handler is very simple:
private void Serializer_UnknownElement(object sender, XmlElementEventArgs e)
{
var group = (DeactivationsGroup)e.ObjectBeingDeserialized;
group.GroupNames.Add(e.Element.Name, new GroupName { Name = e.Element.InnerText });
}
Fiddle.
I have two problem. I need to serialize data to csv and xml but its turn out to be problematic for me.
As xml I desire to get something like:
<sentence>
<word>example1</word>
<word>example2</word>
<word>example3</word>
</sentence>
<sentence>
<word>example1</word>
<word>example2</word>
<word>example3</word>
</sentence>
My data its SentencedModel which contain inside collection of WordsModel. So it like: List<ICollection<string>>. Every position (sentence) in list have collection of string (words).
Class look like:
[Serializable]
public class WordsModel : IEnumerable<string>
{
[XmlRoot("Word")]
public ICollection<string> Words { get; set;}
public IEnumerator<string> GetEnumerator()
{
return this.Words.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.Words.GetEnumerator();
}
}
[Serializable]
public class SentencedModel : IEnumerable<WordsModel>
{
[XmlArray("Sentence"), XmlArrayItem(typeof(WordsModel), ElementName = "Words")]
public ICollection<WordsModel> Sentences { get; set; }
public SentencedModel()
{
this.Sentences = new List<WordsModel>();
}
public void Add(WordsModel words)
{
this.Sentences?.Add(words);
}
public IEnumerator<WordsModel> GetEnumerator()
{
return this.Sentences.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.Sentences.GetEnumerator();
}
}
My class which is Repositories for that:
public class WordsSeperapedBySentence
{
public SentencedModel WordsSeperatedBySentence { get; }
public WordsSeperapedBySentence()
{
this.WordsSeperatedBySentence = new SentencedModel();
}
public bool AddSentence(ICollection<string> words)
{
if (words == null) return false;
WordsModel wordsModel = new WordsModel();
wordsModel.Words = words;
this.WordsSeperatedBySentence.Add(wordsModel);
return true;
}
}
Here is my serializer class:
public class SerializeData
{
public string SerializeToXml(SentencedModel data)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(SentencedModel));
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, data);
return textWriter.ToString();
}
}
public ToCsv(WordsSeperapedBySentence data)
{
//??
}
}
But after using
List<string> example1 = new List<string>();
example1.Add("Chris");
example1.Add("call");
example1.Add("Anna");
List<string> example2 = new List<string>();
example2.Add("Somebody");
example2.Add("call");
example2.Add("Wolf");
WordsModel words1 = new WordsModel();
WordsModel words2 = new WordsModel();
words1.Words = example1;
words2.Words = example2;
SentencedModel sentenced = new SentencedModel();
sentenced.Add(words1);
sentenced.Add(words2);
SerializeData serialize = new SerializeData();
var stringAsResult = serialize.SerializeToXml(sentenced);
Console.WriteLine(stringAsResult);
I got errors. Also I do not have idea how to storage them to CSV.
Could you help me?
Thank you in advance.
In order to save your data as CSV, you can use the following method which provides this output:
Chris,call,Anna
Somebody,call,Wolf
Each line is a sentence then all the words are separated by commas.
public string ToCsv(SentencedModel data)
{
var csvLines = data.Select(x => String.Join(",", x));
var csv = String.Join(Environment.NewLine, csvLines);
return csv;
}
I am still missing the XML part, if I do, I will edit the answer.
At least you have a part of it.
Edit Please find below the ToCsv with the fields being escaped based on the comments below.
public string ToCsv(SentencedModel data)
{
var csvLines = data.Sentences.Select(x => String.Join(",", x.Words.Select(w => EscapeForCsv(w))));
var csv = String.Join(Environment.NewLine, csvLines);
return csv;
}
private string EscapeForCsv(string input)
{
return String.Format("\"{0}\"", input.Replace("\"", "\"\"\""));
}
First: If you want to tokenize a text - I recommend:
use an array and not a list. For example: string[][]. The reason: List will locate 10%-20% more memory. You can convert a List to Array by .ToArray() (e.g. example1.ToArray) or use the C# 6.0 syntax:
string[][] sentence = new [] { {"Chris","called","Anna"}, {"Somebody","called","Wolf"} };
If possible: use the primitive datatypes - classes are to complex and slowdown your textprocessing.
Second: If you want to implement your own serializer try this approce:
public abstract class AbstractSerializer
{
public abstract void Serialize(string[][] model, string path);
}
public class XmlSerializer : AbstractSerializer
{
public override void Serialize(string[][] model, string path)
{
// your stuff
}
}
public class CsvSerializer : AbstractSerializer
{
public string LineSeparator { get; set; } = "\r\n";
public string ValueSeparator { get; set; } = ";";
public override void Serialize(string[][] model, string path)
{
var stb = new System.Text.StringBuilder();
for (int i = 0; i < model.Length; i++)
{
for (int j = 0; j < model[i].Length; j++)
{
// Example output:
// 0;0;Chris
// 0;1;call
// 0;2;Anna
// 1;0;Somebody
// 1;1;call
// 1;2;Wolf
stb.Append(string.Join(ValueSeparator, i, j, model[i][j], LineSeparator));
}
}
}
}
I am trying to create a jumplist for a LongListSelector, I used the AlphaKeyGroup example from MS as a basis.
Once I have created my list I then want to serialize it and store it until the app is next opened.
The problem I have is that the Key is null when I deserialize the ObservableCollection.
I have something like this on my Main Page
public ObservableCollection<AlphaKeyGroup<myClass>> myList { get; set; }
void process()
{
myList = AlphaKeyGroup<myClass>.CreateGroups(System.Threading.Thread.CurrentThread.CurrentUICulture);
getItemsAndAddtoList();
string serializedata = Serialize(myList);
}
void getItemsAndAddtoList()
{
List<myClass> templist=new List<myClass>();
templist.Add(new myClass("Alpha","A in the jumplist"));
templist.Add(new myClass("Beta", "B in the jumplist"));
templist.Add(new myClass("Cappa", "C in the Jumplist"));
addToList(templist.ToArray());
string data = Serialize(myList);
}
void addToList(myClass[] items)
{
if (this.mainList.Dispatcher.CheckAccess())
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(System.Threading.Thread.CurrentThread.CurrentUICulture);
foreach (myClass item in items)
{
int index = 0;
if (slg.SupportsPhonetics)
{
}
else
{
index = slg.GetGroupIndex(item.description);
}
if (index >= 0 && index < myList.Count)
{
myList[index].Add(item);
}
}
}
else
{
this.mainList.Dispatcher.BeginInvoke(new Action<myClass[]>(addToList), new object[] { items });
}
}
public static string Serialize(object obj)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(sw, obj);
return sw.ToString();
}
}
If I break on return sw.ToString(); I can see something like this
<ArrayOfMyClass>
<myClass>
<description>Alpha</description>
<data>A in the jumplist</data>
</myClass>
</ArrayOfMyClass>
As you can see the key from the AlphaKeyGroup class is missing, if I then deserialise this back and set it as the LongListSelector ItemSource I get a Jumplist with No text values, just empty boxes.
public class myClass
{
public string description { get; set; }
public string data { get; set; }
public myClass()
{
}
public myClass(string Desc, string Data)
{
description = Desc;
data = Data;
}
}
public class AlphaKeyGroup<T> : ObservableCollection<T>
{
public delegate string GetKeyDelegate(T item);
public string Key { get; set; }
public AlphaKeyGroup(string key)
{
Key = key;
}
public AlphaKeyGroup()
{
}
public static ObservableCollection<AlphaKeyGroup<T>> CreateGroups(CultureInfo ci)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
ObservableCollection<AlphaKeyGroup<T>> list = new ObservableCollection<AlphaKeyGroup<T>>();
foreach (string key in slg.GroupDisplayNames)
{
list.Add(new AlphaKeyGroup<T>(key));
}
return list;
}
}
I am sure I am missing something simple, but I couldn't find a solution, I think it is to do with the Parameterless constructor, but I am not sure, I am quite new to C#.