Parsing in C# using JSON.Net (very confusing!) - c#
I want to parse the following JSON:
{"0":{"igloo_id":"0","name":"Igloo Removal","cost":"0"},"1":{"igloo_id":"1","name":"Basic Igloo","cost":"1500"},"2":{"igloo_id":"2","name":"Candy Igloo","cost":"1500"},"3":{"igloo_id":"3","name":"Deluxe Blue Igloo","cost":"4000"},"4":{"igloo_id":"4","name":"Big Candy Igloo","cost":"4000"},"5":{"igloo_id":"5","name":"Secret Stone Igloo","cost":"2000"},"6":{"igloo_id":"6","name":"Snow Igloo","cost":"1000"},"8":{"igloo_id":"8","name":"Secret Deluxe Stone Igloo","cost":"5000"},"9":{"igloo_id":"9","name":"Deluxe Snow Igloo","cost":"3000"},"10":{"igloo_id":"10","name":"Bamboo Hut","cost":"3200"},"11":{"igloo_id":"11","name":"Log Cabin","cost":"4100"},"12":{"igloo_id":"12","name":"Gym","cost":"4800"},"13":{"igloo_id":"13","name":"Split Level Igloo","cost":"4600"},"14":{"igloo_id":"14","name":"Candy Split Level Igloo","cost":"4600"},"15":{"igloo_id":"15","name":"Snowglobe","cost":"3700"},"16":{"igloo_id":"16","name":"Ice Castle","cost":"2400"},"17":{"igloo_id":"17","name":"Split Level Snow Igl","cost":"4600"},"18":{"igloo_id":"18","name":"Fish Bowl","cost":"2400"},"19":{"igloo_id":"19","name":"Tent","cost":"2700"},"20":{"igloo_id":"20","name":"Jack O' Lantern","cost":"2700"},"21":{"igloo_id":"21","name":"Backyard Igloo","cost":"4200"},"22":{"igloo_id":"22","name":"Pink Ice Palace","cost":"2400"},"23":{"igloo_id":"23","name":"Ship Igloo","cost":"4300"},"24":{"igloo_id":"24","name":"Dojo Igloo","cost":"1300"},"25":{"igloo_id":"25","name":"Gingerbread House","cost":"2100"},"26":{"igloo_id":"26","name":"Restaurant Igloo","cost":"4800"},"27":{"igloo_id":"27","name":"Tree House Igloo","cost":"4500"},"28":{"igloo_id":"28","name":"Theatre Igloo","cost":"4600"},"29":{"igloo_id":"29","name":"Circus Tent","cost":"0"},"30":{"igloo_id":"30","name":"Snowy Backyard Igloo","cost":"3000"},"31":{"igloo_id":"31","name":"Cave Igloo","cost":"1500"},"32":{"igloo_id":"32","name":"Green Clover Igloo","cost":"2050"},"33":{"igloo_id":"33","name":"Grey Ice Castle","cost":"2400"},"35":{"igloo_id":"35","name":"Cozy Cottage Igloo","cost":"2500"},"36":{"igloo_id":"36","name":"Estate Igloo","cost":"2500"},"37":{"igloo_id":"37","name":"In Half Igloo","cost":"2300"},"38":{"igloo_id":"38","name":"Shadowy Keep","cost":"2400"},"39":{"igloo_id":"39","name":"Dragon's Lair","cost":"3000"},"40":{"igloo_id":"40","name":"Mermaid Cove","cost":"3030"},"41":{"igloo_id":"41","name":"Whale's Mouth","cost":"2700"},"42":{"igloo_id":"42","name":"Trick-or-Treat Igloo","cost":"2000"},"43":{"igloo_id":"43","name":"Deluxe Gingerbread House","cost":"0"},"45":{"igloo_id":"45","name":"Invisible Snowy","cost":"0"},"46":{"igloo_id":"46","name":"Invisible Beach","cost":"0"},"47":{"igloo_id":"47","name":"Invisible Forest","cost":"0"},"48":{"igloo_id":"48","name":"Invisible Mountain","cost":"0"},"49":{"igloo_id":"49","name":"Shipwreck Igloo","cost":"900"},"50":{"igloo_id":"50","name":"Wildlife Den","cost":"900"},"51":{"igloo_id":"51","name":"Medieval Manor","cost":"1200"},"52":{"igloo_id":"52","name":"Warehouse","cost":"950"},"53":{"igloo_id":"53","name":"Pineapple Igloo","cost":"0"},"54":{"igloo_id":"54","name":"Creepy Cavern","cost":"1500"},"55":{"igloo_id":"55","name":"Frost Bite Palace","cost":"0"},"56":{"igloo_id":"56","name":"Fresh Baked Gingerbread House","cost":"2500"},"57":{"igloo_id":"57","name":"Penthouse","cost":"4000"},"58":{"igloo_id":"58","name":"VIP Penthouse","cost":"0"},"59":{"igloo_id":"59","name":"Invisible Age of Dinosaurs","cost":"0"},"60":{"igloo_id":"60","name":"Puffle Tree Fort","cost":"0"},"61":{"igloo_id":"61","name":"Secret Base","cost":"1600"},"62":{"igloo_id":"62","name":"Death Star Igloo","cost":"1000"},"63":{"igloo_id":"63","name":"Beach Party Igloo","cost":"1500"},"64":{"igloo_id":"64","name":"Gymnasium Igloo","cost":"0"},"65":{"igloo_id":"65","name":"Magical Hideout","cost":"1500"},"66":{"igloo_id":"66","name":"Eerie Castle","cost":"2000"},"67":{"igloo_id":"67","name":"Sweet Swirl Igloo","cost":"0"},"68":{"igloo_id":"68","name":"Train Station Igloo","cost":"1100"},"69":{"igloo_id":"69","name":"Main Event Igloo","cost":"1000"},"70":{"igloo_id":"70","name":"CP Airliner","cost":"1200"}}
I need a way to retrieve igloo_id, name and cost.
I've tried the following, but it's not what I want
List<Igloo> igloosList = JsonConvert.DeserializeObject<List<Igloo>>(itemJson);
This is the structure of my class
namespace Canvas
{
public class Igloo
{
public int cost;
public int igloo_id;
public string name;
public int Cost
{
get
{
return cost;
}
}
public int Id
{
get
{
return igloo_id;
}
}
public string Name
{
get
{
return name;
}
}
}
}
I'm not entirely sure I understand what you want. The code you posted generated an exception when I tried running it,
Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List1[ConsoleApplication2.Program+Igloo]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
It did deserialize correctly if I used:
var igloosList = JsonConvert.DeserializeObject<Dictionary<string, Igloo>>( json );
If you want to loop through it you could then use:
foreach( var igloo in igloosList.Values )
Use the JsonProperty attribute :
[JsonProperty(PropertyName = "cost")]
public int Cost
{
get
{
return cost;
}
private set { cost = value; }
}
[JsonProperty(PropertyName = "igloo_id")]
public int Id
{
get
{
return igloo_id;
}
private set { igloo_id = value; }
}
[JsonProperty(PropertyName = "name")]
public string Name
{
get
{
return name;
}
private set { name = value; }
}
Basically you need to say for each property what is the Json key that will match. Plus, you will need to have a setter for each of your property, but it can be private.
Related
Deserializing xml to list object returning null
I'm trying to deserialize xml data using xmlreader into a list object but I am getting a null back from my call. Here is a sample of my xml data... <ExceptionLog> <ExceptionLogData MessageCount="1" SourceDateTime="2016-02-08T09:32:41.713" MinSourceDateTime="2016-02-08T09:32:41.713" DataId="610029" MaxExceptionLogID="610029" MessageText="INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session. Session not found, missing session hash: hX7K7LONeTilw5RfGT432g== This is expected, it can happen if the session has expired and swept away, or if the user logs out, or if its just someone trying to hack in. " MachineName="VERTEXDPORTSQL1" AppDomainName="VTMS.Windows.SalesforceServicingAgent.exe" ProcessName="VTMS.Windows.SalesforceServicingAgent" /> <ExceptionLogData MessageCount="1" SourceDateTime="2016-02-08T09:22:39.340" MinSourceDateTime="2016-02-08T09:22:39.340" DataId="610028" MaxExceptionLogID="610028" MessageText="INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session. Session not found, missing session hash: rtZTrLk2f99iVttLoz31tg== This is expected, it can happen if the session has expired and swept away, or if the user logs out, or if its just someone trying to hack in. " MachineName="VERTEXDPORTSQL1" AppDomainName="VTMS.Windows.SalesforceServicingAgent.exe" ProcessName="VTMS.Windows.SalesforceServicingAgent" /> </ExceptionLog> This is the object class code that I am trying to create... public class ExceptionLog { public ExceptionLog() { ExceptionLogData = new List<ExceptionLogExceptionLogData>(); } public List<ExceptionLogExceptionLogData> ExceptionLogData { get; set; } } public class ExceptionLogExceptionLogData { private DateTime _sourceDateTimeField; private DateTime _minSourceDateTimeField; private uint dataIdField; private uint _maxExceptionLogIdField; private string _messageTextField; private string _machineNameField; private string _appDomainNameField; private string _processNameField; public byte MessageCount { get; set; } public DateTime SourceDateTime { get { return _sourceDateTimeField; } set { _sourceDateTimeField = value; } } public DateTime MinSourceDateTime { get { return _minSourceDateTimeField; } set { _minSourceDateTimeField = value; } } public uint DataId { get { return dataIdField; } set { dataIdField = value; } } public uint MaxExceptionLogID { get { return _maxExceptionLogIdField; } set { _maxExceptionLogIdField = value; } } public string MessageText { get { return _messageTextField; } set { _messageTextField = value; } } public string MachineName { get { return _machineNameField; } set { _machineNameField = value; } } public string AppDomainName { get { return _appDomainNameField; } set { _appDomainNameField = value; } } public string ProcessName { get { return _processNameField; } set { _processNameField = value; } } } And finally here is how I am trying to deserialize the data... using (var dataReader = sqlCommand.ExecuteXmlReader()) { var serializer = new XmlSerializer(typeof(ExceptionLog)); var returnDataList = serializer.Deserialize(dataReader) as List<ExceptionLogExceptionLogData>; return returnDataList; } What have I missed or what am I doing wrong? I have another approach that I can use until I figure this out and that is the old fashioned way of creating my object list and programmatically populating it with my objects on the fly - not very graceful but for the time being it works. TIA
XmlSerializer is defined of type ExceptionLog but you're then casting the result to List var serializer = new XmlSerializer(typeof(ExceptionLog)); var returnDataList = serializer.Deserialize(dataReader) as List<ExceptionLogExceptionLogData>; The casting should be to the type of serializer: var returnDataList = serializer.Deserialize(dataReader) as ExceptionLog; I didn't check all the elements but you should also mark ExceptionLogData with XmlElement attribute. [XmlElement] public List<ExceptionLogExceptionLogData> ExceptionLogData { get; set; } There might be some issues with the other properties but this should address the problem in the question
Expecting element 'root' from namespace ''.. Encountered 'None' with name '', namespace ''
I've been searching for the past 4 hours for ways on how to tackle this problem, and I've not yet found a solution. I'm building an API with .NET and wish to parse JSON information that is sent back from API calls. My current approach does the following: private void PostNewPlayer(HttpContext context) { // Create the serializer context.Request.InputStream.Position = 0; DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(ASPlayer)); ASPlayer p = (ASPlayer)json.ReadObject(context.Request.InputStream); <-- Exception here Int32 playerId = ASPlayerManager.InsertNewPlayer(p); } But I currently get an Exception at the indicated line. I have made sure my class implements the correct serialization namespaces: using System.Runtime.Serialization; using System.IO; using System.Runtime.Serialization.Json; The class I am trying to serialize has had its DataContract and Member fields set accordingly: [DataContract] public class ASPlayer { [DataMember] private string _name; public string player_name { get { return _name; } set { _name = value; } } [DataMember] private string _location; public string player_location { get { return _location; } set { _location = value; } } // Other vars ... public ASPlayer(string name, string location) { _name = name; _location = location; } } However, when I use a HTTP client such as Postman to make a request I get the error stated in the question title
I think you marked the wrong variables, this: [DataMember] private string _name; public string player_name should be this: private string _name; [DataMember] public string player_name
The answer was a very bad mistake on my behalf, but for those who encounter this in future make sure you send your JSON object in Postman through the Raw field. In my case I simply did: { "player_name": "Test", "player_location": "EUW", "player_wins":10, "player_draws":10, "player_losses":15, "player_points":20 } This fixed it
Read values from a non-delimited string into class object
I have a string with the following structure: Student Name________AgeAddress_______________________Bithday___Lvl Example: Jonh Smith 016Some place in NY, USA 01/01/2014L01 As you can see, there is no delimited character like | or , Also, there is no space between fields (if you check, there is no space between Age/Address and Birthday/Level. The size of each field is static so if data's length is less then it will contains white spaces. I have a class that need to be filled with that information: public class StudentData { public char[] _name = new char[20]; public string name; public char[] _age = new char[3]; public string age; public char[] _address = new char[30]; public string address; public char[] _bday = new char[10]; public string bday; public char[] _level = new char[3]; public string level; } Is there any way to do this automatically and dynamically? I mean I really don't want to code like this: myClass.name = stringLine.substring(0,19); myClass.age = stringLine.substring(20,22); That's because I have way more fields that the ones added in this example & way more string lines with other different data. Update: There were supposed to be a lot of spaces between "Smith" and "016", but I don't know how to edit it. Update2: If I use StringReader.Read() I can evade to use substring and indexes, but it isn't still so dynamically because I would need to repeat those 3 lines for each field. StringReader reader = new StringReader(stringLine); reader.Read(myClass._name, 0 myClass._name.Length); myClass.name = new string(myClass._name);
Given your requirement I came up with an interesting solution. All be-it it may be more complex and longer than using the String.SubString() method as stated. However this solution is transferable to other types and other string. I used a concept of Attributes, Properties, and Reflection to parse a string by a Fixed Length and setting the class Properties. Note I did change your StudentData class to follow a more conventional coding style. Following this handy guide on MSDN: http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx Here is the new StudentData class. Note it uses the properties as opposed to fields. (Not discussed here). public class StudentData { string name; string age; string address; string bday; string level; [FixedLengthDelimeter(0, 20)] public string Name { get { return this.name; } set { this.name = value; } } [FixedLengthDelimeter(1, 3)] public string Age { get { return this.age; } set { this.age = value; } } [FixedLengthDelimeter(2, 30)] public string Address { get { return this.address; } set { this.address = value; } } [FixedLengthDelimeter(3, 10)] public string BDay { get { return this.bday; } set { this.bday = value; } } [FixedLengthDelimeter(4, 3)] public string Level { get { return this.level; } set { this.level = value; } } } Note on each of the properties there is an Attribute called FixedLengthDelimeter that takes two parameters. OrderNumber FixedLength The OrderNumber parameter denotes the order in the string (not the position) but the order in which we process from the string. The second parameter denotes the Length of the string when parsing the string. Here is the full attribute class. [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public sealed class FixedLengthDelimeterAttribute : Attribute { public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength) { this.fixedLength = fixedLength; this.orderNumber = orderNumber; } readonly int fixedLength; readonly int orderNumber; public int FixedLength { get { return this.fixedLength; } } public int OrderNumber { get { return this.orderNumber; } } } Now the attribute is simple enough. Accepts the two paramters we discussed eariler in the constructor. Finally there is another method to parse the string into the object type such as. public static class FixedLengthFormatter { public static T ParseString<T>(string inputString) { Type tType = typeof(T); var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0); T newT = (T)Activator.CreateInstance(tType); Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>(); foreach (var property in properties) { var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false); if (atts.Length == 0) continue; dictionary[property] = atts[0] as FixedLengthDelimeterAttribute; } foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber)) { int length = kvp.Value.FixedLength; if (inputString.Length < length) throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short."); string piece = inputString.Substring(0, length); inputString = inputString.Substring(length); kvp.Key.SetValue(newT, piece.Trim(), null); } return newT; } } The method above is what does the string parsing. It is a pretty basic utility that reads all the properties that have the FixedLengthDelimeter attribute applied a Dictionary. That dictionary is then enumerated (ordered by OrderNumber) and then calling the SubString() method twice on the input string. The first substring is to parse the next Token while the second substring resets the inputString to start processing the next token. Finally as it is parsing the string it is then applying the parsed string to the property of the class Type provided to the method. Now this can be used simply like this: string data1 = "Jonh Smith 016Some place in NY, USA 01/01/2014L01"; StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1); What this does: Parses a string against property attributes in a fixed length format. What this does not do: It does convert the parsed strings to another type. Therefore all the properties must be a string. (this can be easily adapted by adding some type casting logic in). It is not well tested. This is only tested against a few samples. It is not by all means the only or best solution out there.
You could use FileHelpers library (NuGet). Just define the structure of your input file with attributes: [FixedLengthRecord] public class StudentData { [FieldFixedLength(20)] [FieldTrim(TrimMode.Right)] public string name; [FieldFixedLength(3)] public string age; [FieldFixedLength(30)] [FieldTrim(TrimMode.Right)] public string address; [FieldFixedLength(10)] public string bday; [FieldFixedLength(3)] public string level; } Then simply read the file using FileHelperEngine<T>: var engine = new FileHelperEngine<StudentData>(); var students = engine.ReadFile(filename);
Avoid expanding linked objects when serialising
I am using JSON.NET to serialize some c# objects into JSON (and then write to a file). My two main classes are: public class Reservoir { private Well[] mWells; public Well[] wells { get { return mWells; } set { mWells = value; } } } and public Well() { private string mWellName; private double mY; private double mX; public string wellName { get { return mWellName; } set { mWellName = value; } } public double y { get { return mY; } set { mY = value; } } public double x { get { return mX; } set { mX = value; } } private Well[] mWellCorrelations; } The problem is that the output looks like: '{"wells":[{"wellName":"B-B10","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B12","y":152.71005958395972,"x":459.02158140110026,"wellCorrelations":[{"wellName":"B-B13","y":475.0,"x":495.14804408905263,"wellCorrelations":[{"wellName":"B-B11","y":25.0,"x":50.0,"wellCorrelations":[]} i.e. the associated wells of each well object are expanded as objects themselves and this becomes a serious problem of space and time when there lots of associated objects. I suppose I would have preferred something like: '{"wells":[{"wellName":"B-B10","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B12"}], {"wellName":"B-B11","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B13"} i.e maintaining only the well name as the link (assume its unique). Is there a way to do this with JSON.NET? You have set serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; but it doesn't make any difference.
You could add a new readonly property called WellCorrelations that only got the names of the well correlations, and slap a JsonIngore attribute on your mWellCorrelations, like so: [JsonIgnore] private Well[] mWellCorrelations; public string[] WellCorrelations { get { return mWellCorrelations.Select(w => w.wellName).ToArray(); } } http://james.newtonking.com/projects/json/help/html/ReducingSerializedJSONSize.htm That way, the serializer will only serialize the names of the correlated wells.
Developing a Products Class with a nested class .Net
I am looking for help in determining if the class model that I am building can be improved upon. The class that I am building is a simple Product class with a few attributes. class clsProducts { private string _name; private double _productionRate; //Constructor public clsProducts() { _name = "null"; _productionRate = 0.0; } public clsProducts(string name, double productionRate) { _name = name; _productionRate = productionRate; } //Properties public string Name { get { return _name; } } public double ProductionRate { get { return _productionRate; } } } What I would like to add is the ability to have the monthly forecasted values for each product in the class. I could add the following to do this private double _janValue; private double _febValue; and so on, but this seems messy. I also considered creating a nested class called ForecastValues, such as class clsProducts { ...code here.... protected class ForecastValues { private string name; private double forecastValue; ...other code..... } } however, I am not sure that this idea would even work. Can any one suggest a way for me to handle this cleanly? Thank you
A few things here. I would recommend removing the cls hungarian prefix from the class name. Depending on exactly what your "ForecastValues" are. You could make a property on the "Product" class that is a List, or possibly a Dictionary. My guess is that you might be able to go the dictionary route with ease.
I would suggest just to use an array and an indexer. public enum Month { January = 1, February = 2, March = 3, April = 4, May = 5, June = 6, July = 7, August = 8, September = 9, October = 10, November = 11, December = 12 } public class Product { private readonly String name = null; private readonly Double productionRate = 0.0; private readonly Double[] productionRateForcast = new Double[12]; public Product(String name, Double productionRate) { this.name = name; this.productionRate = productionRate; } public String Name { get { return this.name; } } public Double ProductionRate { get { return this.productionRate; } } public Double this[Month month] { get { return this.productionRateForcast[month - Month.January]; } set { this.productionRateForcast[month - Month.January] = value; } } } I am not sure if month - Month.January requires an explicit cast to Int32. Alternativly one could start with January = 0 but this seems a bit odd, too. I did also some code changes. I removed the default constructor, because I see no value in a Product instance with "uninitialized" fields and no possibilty to alter them later. In consequence I made the fields readonly, too. Finaly I removed the Hungarion notation prefix - this is a quite an outdate coding style - and turned Products into Product because it represents one product not a collection of products. UPDATE To catch up the dictionary idea .... I will just give the required changes. private readonly IDictionary<Month, Double> productionRateForcast = new Dictionary<Month, Double>(); public Double this[Month month] { get { return this.productionRateForcast[month]; } set { this.productionRateForcast[month] = value; } } This might be a even cleaner solution then using an array. You could also just expose the dictionary through a property instead of having an indexer, but I consider the indexer a cleaner solution because it hides some implementation details. public IDictionary<Month, Double> ProductionRateForcast { return this.productionForecast; } In all case the usage would be as follows. Product myProduct = new Product("Great Product", 0.8); myProduct[Month.August] = 0.7; This looks quite odd. One could try adding a IndexerNameAttribute to the indexer, but I am not sure if this would allow to write myProduct.ProductionValueForcast[Month.August] = 0.7; in a language with indexer support. So I finally tend to change my mind and prefer exposing the dictionary by a property if the IndexerNameAttribute does not help.
I don't think nested classes are a great idea. What I would do is create an additional class 'ForecastValues' but mark it as 'internal protected'. That way you can use it within your assembly but users of your code will only be able to reference it when it contains values. -Shaun
This is what I would do, class ClsProducts { //Constructor public ClsProducts() { Name = "null"; ProductionRate = 0.0; } public ClsProducts(string name, double productionRate) { Name = name; ProductionRate = productionRate; } //Automatic properties with private setters public string Name { get; private set; } public double ProductionRate { get; private set; } //since you basically have key value pair, why not use one? public KeyValuePair<String,Double> Forcast{ get; set; } }