C# Deserialize List<T> Issue - c#

This is the class I am trying to serialize and deserialize:
[Serializable]
[XmlRoot("RecipeStepWords")]
public class CRecipeStepsWords
{
[XmlArray]
public List<RecipeStep> ReadRecipeSteps { get; set; }
[XmlArray]
public List<RecipeStep> WriteRecipeSteps { get; set; }
public CRecipeStepsWords()
{
ReadRecipeSteps = new List<RecipeStep>();
for (int x = 1; x <= 8; x++)
{
ReadRecipeSteps.Add(new RecipeStep());
}
WriteRecipeSteps = new List<RecipeStep>();
for (int x = 1; x <= 6; x++)
{
WriteRecipeSteps.Add(new RecipeStep());
}
}
public class RecipeStep
{
public String Test1 { get; set; }
public String Test2 { get; set; }
public RecipeStep()
{
Test1= "Sample 1";
Test2 = "Sample 2";
}
}
}
As you can see from the code, ReadRecipeSteps should have 8 items and WriteRecipeSteps should have 6. However, whenever I run the program multiple times, the deserialized list gets added to,i.e., first run = ReadRecipeStesp.Count = 8, second run .Count = 16, etc.
Here is how I deserialize:
if (File.Exists(Path.Combine(path, fName)))
{
StreamReader objStreamReader = new StreamReader(Path.Combine(path, fName));
XmlSerializer x = new XmlSerializer(RecipeStepsWords.GetType());
RecipeStepsWords = (CRecipeStepsWords)x.Deserialize(objStreamReader);
objStreamReader.Close();
}
I'm using VS 2010 for this. What I would like is to always have the correct number of items in the lists, 8 and 6, instead of having them added to every time it gets deserialized. Any help with this would be appreciated.

Try adding an
[XmlArrayItem("ReadReceipe")] right after your xml array. Same for the WriteReciepe
So like this.
[XmlArray]
[XmlArrayItem("ReadReceipe")]
public List<RecipeStep> ReadRecipeSteps { get; set; }
I think what happens is you just keep adding those steps to the root, without having a parent node.
Let me know.

// Reset your object
RecipeStepsWords = new CRecipeStepsWords();
RecipeStepsWords = (CRecipeStepsWords)x.Deserialize(objStreamReader);
objStreamReader.Close();

Related

Adding items to ObservableColletion - UWP C#

When I'm trying to add items to observable collection, it always replace all items with items added last. What is the reason for it? My code is here
public class FavoriteClassList
{
public int ID { get; set; }
public string Name { get; set; }
}
public static ObservableCollection<FavoriteClassList> _FavoriteClassList = new ObservableCollection<FavoriteClassList>();
FavoriteClassList objFavoriteClassList = new FavoriteClassList();
for (int m=1;m<=10;m++)
{
objFavoriteClassList.ID = m;
objFavoriteClassList.Name = "Name"+m;
_FavoriteClassList.Add(objFavoriteClassList);
}
Now when printing values of AppGlobals._FavoriteClassList it shows 10 items. But ID and Name of each items is always 10 and Name10 respectively.
You added one object 10 times and rewrite it 10 times. Here is a fixed version:
public class FavoriteClassList
{
public int ID { get; set; }
public string Name { get; set; }
}
public static ObservableCollection<FavoriteClassList> _FavoriteClassList = new ObservableCollection<FavoriteClassList>();
for (int m=1;m<=10;m++)
{
FavoriteClassList objFavoriteClassList = new FavoriteClassList();
objFavoriteClassList.ID = m;
objFavoriteClassList.Name = "Name"+m;
_FavoriteClassList.Add(objFavoriteClassList);
}

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.

How to parse DbGeometry object into List<T>?

I'm trying to parse a polygon, which is presented as DbGeometry class (from the System.Data.Entity.Spatial) to some List representation, but there were fails.
I've tried:
- to convert it with the method .ToList<>()
- to parse using JSON library in .NET, but have sample code from different websites failed with deserializing DbGeometry
So, right now I'm returning geometry as a string in my Web API application:
If I couldn't find any solution how, to represent it as list of doubles I will have to parse manually it in JavaScript, which way I think is very incorrect and there must be some solution.
I'm using Entity Framework v.6.1.1, which has prepared the next model:
public partial class Buildings
{
public int id { get; set; }
public Nullable<bool> has_holes { get; set; }
public System.Data.Entity.Spatial.DbGeometry polygon_data { get; set; }
public System.Data.Entity.Spatial.DbGeometry position_wgs { get; set; }
public System.Data.Entity.Spatial.DbGeometry position_mercator { get; set; }
public Nullable<int> height { get; set; }
public string street_name { get; set; }
public System.Data.Entity.Spatial.DbGeometry holes_data { get; set; }
public Nullable<double> angle { get; set; }
}
I've shown it, if you want to see a structure of the table from MSSQL CE (which is an embedded db, or LocalDb, another name).
So I want:
System.Data.Entity.Spatial.DbGeometry polygon_data
System.Data.Entity.Spatial.DbGeometry holes_data
Be prepared as lists of doubles, so my question is: How can I parse DbGeometry instance, which holds a collection of points into List<Point>?.
I had similar problem. What I did was, created extension method that parse the given geometry data to points. #Erik Philips also have nice solution though. This is what I came up with.
public static class ExtensionString
{
private static Dictionary<string, string> _replacements = new Dictionary<string, string>();
static ExtensionString()
{
_replacements["LINESTRING"] = "";
_replacements["CIRCLE"] = "";
_replacements["POLYGON"] = "";
_replacements["POINT"] = "";
_replacements["("] = "";
_replacements[")"] = "";
}
public static List<Point> ParseGeometryData(this string s)
{
var points = new List<Point>();
foreach (string to_replace in _replacements.Keys)
{
s = s.Replace(to_replace, _replacements[to_replace]);
}
string[] pointsArray = s.Split(',');
for (int i = 0; i < pointsArray.Length; i++)
{
double[] coordinates = new double[2];
//gets x and y coordinates split by space, trims whitespace at pos 0, converts to double array
coordinates = Array.ConvertAll(pointsArray[i].Remove(0, 1).Split(null), double.Parse);
points.Add(new Point() { X = coordinates[0], Y = coordinates[1] });
}
return points;
}
}
And just use it like this
List<System.Drawing.Point> points = geometryDataStr.ParseGeometryData();
If your Geometry is Valid then you can do:
class Program
{
static void Main(string[] args)
{
DbGeometry test = DbGeometry.FromText("POLYGON((1 1, 1 2, 3 3, 1 1))");
var foo = test.AsText();
var points = new List<Point>();
Console.WriteLine(foo);
if (foo.StartsWith("POLYGON ((")
&& foo.EndsWith("))"))
{
foo = foo.Substring(10, foo.Length - 12);
var rawPoints = foo.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
foreach (var rawPoint in rawPoints)
{
var splitPoint = rawPoint.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
points.Add(new Point() { X = decimal.Parse(splitPoint[1]), Y = decimal.Parse(splitPoint[0]) });
}
}
foreach (var point in points)
{
Console.WriteLine(point.ToString());
}
Console.ReadKey();
}
}
class Point
{
public decimal X { get; set; }
public decimal Y { get; set; }
public override string ToString()
{
return string.Format("[X={0}],[Y={1}]", X, Y);
}
}
result:
POLYGON ((1 1, 1 2, 3 3, 1 1))
[X=1],[Y=1]
[X=2],[Y=1]
[X=3],[Y=3]
[X=1],[Y=1]

Multiple elements with the same name, with multiple children

I have come across a problem, I need to basicly deserialize this:
<?xml version="1.0" encoding="UTF-8"?>
<api_data>
<status>ok</status>
<sessions>
<id>2</id>
<sessionID>6bfd1f1a7e87a8a6ed476234ad1d6e86</sessionID>
<gameID>1</gameID>
<maxPlayers>8</maxPlayers>
<hostIP>12.0.0.1</hostIP>
<hostPort>1993</hostPort>
<inProgress>0</inProgress>
<timestamp>1358894690</timestamp>
</sessions>
<sessions>
<id>3</id>
<sessionID>eeb4dc2df32f885c2b7d13f28a246830</sessionID>
<gameID>1</gameID>
<maxPlayers>8</maxPlayers>
<hostIP>12.0.0.1</hostIP>
<hostPort>1993</hostPort>
<inProgress>0</inProgress>
<timestamp>1358894732</timestamp>
</sessions>
</api_data>
And I need to convert that to usable data, its also dynamic, so there could be more than just 2 session elements, there could be 4, 20, or 0, the code I have now is just broken, and I was wondering whats a good method to get this to work?
Currently I am up to the point of the XDocument class, with all this loaded.
And I need to return a multi-dimensional array with this data.
EDIT:
Current code, completely broken:
var xmlSessions = xmlDATA.Descendants("api_data").Elements("sessions").Select(x => x);
result = new string[xmlDATA.Descendants("api_data").Count(), 7];
EDIT 2:
More info
The way I was thinking the MultiDimensional Array would be is as follows:
array[0,0] "ok" //Status
array[1,0 to 7] //First Session details go here
array[2,0 to 7] //Second session details go here, and so forth.
You can define the following class representations:
public class api_data
{
public string status { get; set; }
[XmlElement]
public session[] sessions { get; set; }
}
public class session
{
public int id { get; set; }
public string sessionID { get; set; }
public int gameID { get; set; }
public int maxPlayers { get; set; }
public string hostIP { get; set; }
public int hostPort { get; set; }
public int inProgress { get; set; }
public int timestamp { get; set; }
}
The key is the [XmlElement] tag on the sessions property, that will instruct the XmlSerializer to read/write XML using the schema sample you provided. To deserialize it, you can use the XmlSerializer as such:
//this might change, not sure how you obtain your xml,
//but let's assume you already have it available as a string
byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlData);
var stream = new MemoryStream(xmlBytes);
XmlSerializer serializer = new XmlSerializer(typeof(api_data));
api_data apidata = (api_data)serializer.Deserialize(stream);
Don't need any more XML adornment or setup than that to read it in (tested and working).
EDIT: Though you may want to consider using some other XML attributes to transfer to some nicer naming conventions, and we can also List<Session> to boot instead of an array:
[XmlRoot("api_data")]
public class ApiData
{
[XmlElement("status")]
public string Status { get; set; }
[XmlElement("sessions")]
public List<Session> Sessions { get; set; }
}
public class Session
{
[XmlElement("id")]
public int ID { get; set; }
[XmlElement("sessionID")]
public string SessionID { get; set; }
[XmlElement("gameID")]
public int GameID { get; set; }
[XmlElement("maxPlayers")]
public int MaxPlayers { get; set; }
[XmlElement("hostIP")]
public string HostIP { get; set; }
[XmlElement("hostPort")]
public int HostPort { get; set; }
[XmlElement("inProgress")]
public int InProgress { get; set; }
[XmlElement("timestamp")]
public int TimeStamp { get; set; }
}
EDIT: Just noticed that you need to turn this into a multidimensional array (not sure why, but you specify that's legacy). Well at this point, you have a nice object model from which you can do this data transfer. Not sure how you do the typing, but let's just assuming object type array for now:
ApiData apiData = DeserializeMyApiData(); // from above
array[0][0] = apiData.Status;
for(int i = 1; i <= apiData.Sessions.Count; i++)
{
var session = apiData.Sessions[i - 1];
array[i] = new object[8];
array[i][0] = session.ID;
array[i][1] = session.SessionID;
array[i][2] = session.GameID;
array[i][3] = session.MaxPlayers;
array[i][4] = session.HostIP;
array[i][5] = session.HostPort;
array[i][6] = session.InProgress;
array[i][7] = session.TimeStamp;
}
That will go through and build up your array regardless of how many sessions you have.
Can you wrap the 'sessions' tags inside a 'session_list' tag?
If so you could use something like this to load it:
public class api_data {
public class sessions {
public string id { get; set; }
public string sessionID { get; set; }
// put all the other vars in here ...
}
public string status { get; set; }
public List<sessions> session_list { get; set; }
public static api_data LoadFromXML(string xmlFile) {
api_data localApiData;
// serialize from file
try {
var xs = new XmlSerializer(typeof(api_data),
new XmlRootAttribute("api_data"));
using (TextReader tr = new StreamReader(xmlFile)) {
localApiData= xs.Deserialize(tr) as api_data;
}
}
catch (Exception ex) {
Log.LogError(string.Format(
"Error reading api_data file {0}: {1}",
xmlFile, ex.Message));
return null;
}
return localApiData;
}
}
If you cannot change the format of the xml file, you might need to load the status in it's own step and then load the sessions as if the api-data was the list variable, although the fact the status is there, might give you an error.
If you really only want a multidimensional array, you can obtain this from that XML with a single (somewhat long) line of code:
string[][] items = XDocument.Parse(xml).Root.Elements().Select(e => e.HasElements ? e.Elements().Select(ei => ei.Value).ToArray() : new string[]{ e.Value }).ToArray();
Or to make that same single statement a bit more readable:
string[][] items =
XDocument.Parse(xml).Root
.Elements().Select(e => e.HasElements ?
e.Elements().Select(ei => ei.Value).ToArray() : new string[]{ e.Value })
.ToArray();
From that source XML, this would produce an array like this:
string[][]
{
{ "ok" },
{ "2", "6bfd1f1a7e87a8a6ed476234ad1d6e86", "1", "8", "12.0.0.1", "1993", "0", "1358894690" },
{ "3", "eeb4dc2df32f885c2b7d13f28a246830", "1", "8", "12.0.0.1", "1993", "0", "1358894732" }
}
If you want to get the status separately, and put the other values in a multidimensional array, you could do this:
XDocument doc = XDocument.Parse(xml);
string status = doc.XPathSelectElement("/*/status").Value;
string[][] items =
doc.Root.Elements().Where(e => e.HasElements)
.Select(e => e.Elements().Select(ei => ei.Value).ToArray()).ToArray();
This would produce the same as above, except status would be an individual string, and items would not have the first single-element array in it:
string[][]
{
{ "2", "6bfd1f1a7e87a8a6ed476234ad1d6e86", "1", "8", "12.0.0.1", "1993", "0", "1358894690" },
{ "3", "eeb4dc2df32f885c2b7d13f28a246830", "1", "8", "12.0.0.1", "1993", "0", "1358894732" }
}

Serialize And Deserialize Object Graph To and From String Key/Value Pair

Assuming the following model
public class MyObject
{
public string Name { get; set; }
public ICollection<MyObjectItem> Items { get; set; }
}
public class MyObjectItem
{
public string Name { get; set; }
public int Total { get; set; }
}
I want to serialize and deserialize this object graph to a list of key/value pair of strings like :
MyObject.Name - "Name"
MyObject.Items.0.Name - "Name1"
MyObject.Items.0.Total - "10"
MyObject.Items.1.Name - "Name2"
MyObject.Items.1.Total - "20"
Object serialization is usually expensive for big xml structures. If it's possible, try to use XMlWriter or XmlTextWriter - usage example: http://dotnetperls.com/xmlwriter
Well, you could not use a built-in serializer for that, you would need a custom ToString() / Parse(), similar to this: (ToString() is kind of self explanatory)
MyObject obj = new MyObject();
List<MyObjectItem> items = new List<MyObjectItem>();
foreach (string line in text.Split)
{
// skip MyObject declaration int idx = line.IndexOf('.');
string sub = line.Substring(idx);
if (sub.StartsWith("Name")) {
obj.Name = sub.Substring("Name".Length + 3 /* (3 for the ' - ' part) */);
}
else
{
sub = sub.Substring("Items.".Length);
int num = int.Parse(sub.Substring(0, sub.IndexOf('.'));
sub = sub.Substring(sub.IndexOf('.' + 1);
if (items.Count < num)
items.Add(new MyObjectItem());
if (sub.StartsWith("Name"))
{
items[num].Name = sub.SubString("Name".Length + 3);
}
else
{
items[num].Total = sub.SubString("Total".Length + 3);
}
}
}
obj.Items = items;
Hope this helps, as I do not have access to a C# IDE at this time...

Categories

Resources