Can't deserialize an object using Newtonsoft.Json.Linq; - c#

I receive a string in this format:
{
"ok": true,
"quote": { // the below is the same as returned through the REST quote API
"symbol": "FAC",
"venue": "OGEX",
"bid": 5100, // best price currently bid for the stock
"ask": 5125, // best price currently offered for the stock
"bidSize": 392, // aggregate size of all orders at the best bid
"askSize": 711, // aggregate size of all orders at the best ask
"bidDepth": 2748, // aggregate size of *all bids*
"askDepth": 2237, // aggregate size of *all asks*
"last": 5125, // price of last trade
"lastSize": 52, // quantity of last trade
"lastTrade": "2015-07-13T05:38:17.33640392Z", // timestamp of last trade,
"quoteTime": "2015-07-13T05:38:17.33640392Z" // server ts of quote generation
}
}
I want to use the Newtonsoft.Json.Linq way of deserializing for performance reasons. When I try to turn the Json string into a Quote object using the following method:
public static Quote QuoteFromJson(string quoteJson)
{
var json = JObject.Parse(quoteJson);
var quote = json["quote"].
Select(q => new Quote
{
Ask = int.Parse(q["ask"].ToString()),
AskDepth = int.Parse(q["askDepth"].ToString()),
AskSize = int.Parse(q["askSize"].ToString()),
Bid = int.Parse(q["bid"].ToString()),
BidDepth = int.Parse(q["bidDepth"].ToString()),
BidSize = int.Parse(q["bidSize"].ToString()),
Last = int.Parse(q["last"].ToString()),
LastSize = int.Parse(q["lastSize"].ToString()),
LastTrade = q["lastTrade"].ToString(),
QuoteTime = q["quoteTime"].ToString(),
Symbol = q["symbol"].ToString(),
}).First();
return quote;
}
This gives an error message:
Cannot access child value on Newtonsoft.Json.Linq.JProperty
What am I doing wrong?

Here is a direct answer to your question:
The quote variable corresponds to the quote token in your JSON. This is a single item and not a collection, so you shouldn't treat it as a collection and use the Select method.
Instead, access it directly like this:
var json = JObject.Parse(quoteJson);
var quote = json["quote"];
var result = new Quote
{
Ask = int.Parse(quote["ask"].ToString()),
AskDepth = int.Parse(quote["askDepth"].ToString()),
AskSize = int.Parse(quote["askSize"].ToString()),
Bid = int.Parse(quote["bid"].ToString()),
BidDepth = int.Parse(quote["bidDepth"].ToString()),
BidSize = int.Parse(quote["bidSize"].ToString()),
Last = int.Parse(quote["last"].ToString()),
LastSize = int.Parse(quote["lastSize"].ToString()),
LastTrade = quote["lastTrade"].ToString(),
QuoteTime = quote["quoteTime"].ToString(),
Symbol = quote["symbol"].ToString(),
};

Your problem is that you are iterating through the children of "quote" with your Select, then retrieving their properties by name. This would be appropriate if "quote" were an array, but it's not -- it's already a single object. Thus you should do:
var rootObj = JObject.Parse(quoteJson);
var quoteObj = rootObj["quote"];
var quote = new Quote
{
Ask = (int)quoteObj["ask"],
AskDepth = (int)quoteObj["askDepth"],
AskSize = (int)quoteObj["askSize"],
Bid = (int)quoteObj["bid"],
BidDepth = (int)quoteObj["bidDepth"],
BidSize = (int)quoteObj["bidSize"],
Last = (int)quoteObj["last"],
LastSize = (int)quoteObj["lastSize"],
LastTrade = (string)quoteObj["lastTrade"],
QuoteTime = (string)quoteObj["quoteTime"],
Symbol = (string)quoteObj["symbol"],
};
Note I am using explicit casting. This handles internationalization of numbers in a manner consistent with the JSON standard. int.Parse() parses using localized formatting, which is not guaranteed to be consistent with the standard in all locales.
However, it is much simpler to use deserialization:
var rootObj = JObject.Parse(quoteJson);
var quoteObj = rootObj["quote"];
var quote = quoteObj.ToObject<Quote>();
I suggest you should time this to make sure it really is slower than Linq to JSON.

Related

How to get character info from a file?

I created a file which named by a given name by user, and add some stats in it for example : healt, power, speed, inventory like so. Now, i want to create a function that get stats from given path. It need find the file from given name and get the stat.
I tried re-read the file as note/json so on and it didnt work very well. I want to get variables like :
if(inventory.Contains("Bread"))
Note : I tried to save files as .json but it saved as unknown note i dont know how and why.
...
...
CharacterData Character = new CharacterData()
{
health = 100,
power = 100,
speed = 100,
dexterity = 100,
hungry = 100,
weapon = "Fist",
inventory = new List<string>()
{
"Bread"
}
};
string stringjson = JsonConvert.SerializeObject(Character);
File.WriteAllText(characterPath, stringjson);
}
public int GetCharInfo(string charName,string stat)
{
//return (stat value)
}
you can do something like this:
CharacterData Character = new CharacterData()
{
health = 100,
power = 100,
speed = 100,
dexterity = 100,
hungry = 100,
weapon = "Fist",
inventory = new List<string>()
{
"Bread"
}
};
string stringjson = JsonConvert.SerializeObject(Character);
string path = #"C:\DEV\StackOverflow\";
string characterPath = path + "johnny.json";
File.WriteAllText(characterPath, stringjson);
public int GetCharInfo(string charName, string stat)
{
string json = File.ReadAllText(path + $"{charName}.json");
JObject obj = JObject.Parse(json);
return (int)obj[stat];
}
now if you call:
GetCharInfo("johnny", "power")
you will get the value:
100
also, if you want to see if the key exists you can use the ContainsKey method on JObject like such:
if(obj.ContainsKey(stat))
return (int)obj[stat];
UPDATE
if you want to get a "stat" of different type I suggest doing this:
have this method that gets the character as a json object
public JObject GetCharacterJSON(string charName){
string json = File.ReadAllText(path + $"{charName}.json");
return JObject.Parse(json);
}
now you can load this inside a variable
JObject johnny = GetCharacterJSON("johnny);
And you can get the inventory property like such:
List<string> inventory = johnny["inventory"].ToObject<List<string>>();
As a suggestion, from my point of view if I were you I would try to deserialize the json string into a CharacterData object and then use that like this:
public CharacterData GetCharacterData(string charName){
string json = File.ReadAllText(path + $"{charName}.json");
return JsonConvert.DeserializeObject<CharacterData>(json);
}
CharacterData characterData = GetCharacterData("johnny");
int power = characterData.power; //100
List<string> inventory = characterData.inventory; //["Bread"]
if(inventory.Contains("Bread"){
//do something
}
Note: I didn't take into account error handling in all examples for when the file does not exist or property does not exist etc. I've just outlined the general principle in order to be simple to understand.
Thanks
I guess begin with
File.ReadAllText(path)
Example of usage and documentation
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltext?view=net-6.0
Then you can covert the result to JSON and extract the information that you want

Querying a list of strings with a query string?

I have a dictionary:
<string,List<string>>
The key is the product code say "product1" then the list is a list of properties:
"Brand","10.40","64","red","S"
Then I 'can' have a list of rules/filters e.g.
var tmpFilter = new customfilters();
tmpFilter.Field = "2";
tmpFilter.Expression = ">";
tmpFilter.Filter = "10";
So for the above example this would pass because at index 2 (tmpFilter.Field) it is more than 10; then I have another object which defines which fields within the list I want to write to file. For that dictionary item I just want to write the product brand and price where the filters match.
At the moment without the filter I have:
var tmp = new custom();
tmp.Columns = "0,1";
tmp.Delimiter = ",";
tmp.Extention = ".csv";
tmp.CustomFilters = new List<customfilters>() {new customfilters(){ Field = "2", Expression = ">", Filter = "10"} };
public static void Custom(custom custom)
{
foreach (var x in Settings.Prods)
{
//Get Current Product Code
var curprod = Settings.ProductInformation[x];// the dictionary value
foreach (var column in custom.Columns)
{
var curVal = curprod[Convert.ToInt32(column)];
tsw.Write(curVal + custom.Delimiter);
}
Settings.Lines++;
tsw.WriteLine();
}
tsw.Close();
}
I only want to write the curprod if all the filters pass for that list of strings.
How I can do this?
There's a really nice Nuget package based on an example published by Microsoft, that they have decided to make really hard to find for some reason, that allows dynamic linq queries:
https://www.nuget.org/packages/System.Linq.Dynamic/1.0.2
Source:
https://github.com/kahanu/System.Linq.Dynamic
Using that you can do stuff like this very easily (note: I used strings here because the OP states they have a List<string>):
List<string> stuff = new List<string> { "10.40", "64", "5", "56", "99", "2" };
var selected = stuff.Select(s => new { d = double.Parse(s) }).Where("d > 10");
Console.WriteLine(string.Join(", ", selected.Select(s => s.d.ToString()).ToArray()));
Outputs:
10.4, 64, 56, 99
That may give you a place to start. One thing you are going to have to tackle is identifying which of your fields are numeric and should be converted to a numeric type before trying to apply your filter. Otherwise you are going to comparing as strings.

Store each parsed value of JSON into an array

I'm working on the Array in C#, as following code below, the uricontent is a List in which each string contains one JSON value, I could parse the content, however, I want to have one other array or List to store each parsed value, in the example below, rooms variable can store each time one JSON parsed value, now I wish to store those parsed values in one array.
int i = 0;
while (uricontent.Count != 0)
{
var rooms = JObject.Parse(uricontent[i].ToString())
["rooms"]
.Select(x => new
{
roomID = (string)x["room_name"],
Name = WebUtility.HtmlDecode((string)x["room_name"]),
Price = PriceHelper.Convert((string)x["discountedTotal"]),
Currency = (string)x["currency"],
Occupan = (int)x["adult"]
}).ToArray();
i++;
}
rooms {<>f_AnonymousType11[1]<>f_AnonymousType11[]
[0] { roomID = "Superior 1st floor", Name = "Superior 1st floor", Price = 207.4, Currency = "EUR", Occupan = 2 }
As indicating above, the rooms overwrite the data in each iteration, how can I store those values in one other array like
[1].....
[2].....
....
Thanks
I think what you need is the SelectMany method. SelectMany concatenates all of the IEnumerables generated by the inner Select statements, and returns them as a single IEnumerable, which can then be converted into an array:
var rooms = uricontent
.SelectMany(
uriContentElementJson =>
{
JObject uriContentElement = JObject.Parse(uriContentElementJson);
return uriContentElement["rooms"].Select(
room => new
{
RoomID = (string)room["room_name"],
Name = WebUtility.HtmlDecode((string)room["room_name"]),
Price = PriceHelper.Convert((string)room["discountedTotal"]),
Currency = (string)room["currency"],
Occupant = (int)room["adult"]
});
})
.ToArray();

How to avoid "Duplicate type name within an assembly" while reusing ScriptEngine with anonymouse type?

I'm working with scripting rules engine based on roslyn-ctp that will process IEnumerable<T> and returns results as IEnumerable<T>. To avoid creating of ScriptEngine, configuring and parsing again and again, I'd like to reuse one instance of ScriptEngine.
Here is a short sample (full sample at gist.github.com):
var engine = new ScriptEngine();
new[]
{
typeof (Math).Assembly,
this.GetType().Assembly
}.ToList().ForEach(assembly => engine.AddReference(assembly));
new[]
{
"System", "System.Math",
typeof(Model.ProcessingModel).Namespace
} .ToList().ForEach(#namespace => engine.ImportNamespace(#namespace));
IEnumerable<Model.ProcessingModel> models = new[]
{
new Model.ProcessingModel { InputA = 10M, InputB = 5M, Factor = 0.050M },
new Model.ProcessingModel { InputA = 20M, InputB = 2M, Factor = 0.020M },
new Model.ProcessingModel { InputA = 12M, InputB = 3M, Factor = 0.075M }
};
// no dynamic allowed
// anonymous class are duplicated in assembly
var script =
#"
Result = InputA + InputB * Factor;
Delta = Math.Abs((Result ?? 0M) - InputA);
Description = ""Some description"";
var result = new { Σ = Result, Δ = Delta, λ = Description };
result
";
// Here is ArgumentException `Duplicate type name within an assembly`
IEnumerable<dynamic> results =
models.Select(model => engine.CreateSession(model).Execute(script));
And here are several issues:
roslyn-ctp does not support dynamic keyword
While using anonymous types inside script I get an exception Duplicate type name within an assembly when rsolyn-ctp is creating assembly using System.Reflection.Emit
Question
Is there way to create ScriptEngine and reuse it many times when script contains anonymous type?
You're hitting a bug in Roslyn.
I'd recommend not compiling the script in a loop. Since the code doesn't change it is much more efficient to only update the data the script is working with. Such approach also avoids the bug.
var model = new Model();
var session = engine.CreateSession(model);
var submission = session.CompileSubmission<dynamic>(script);
foreach (Model data in models)
{
model.InputA = data.InputA;
model.InputB = data.InputB;
model.Factor = data.Factor;
dynamic result = submission.Execute();
Console.WriteLine("{0} {1} {2}", result.Σ, result.Δ, result.λ);
}

Need a Recursive Function to Solve a Typical Ecommerce Multi-Variation Problem

Let's say I have the following data (in pseudo-code for readability):
var myVariations = [
{ Name = "Color", Values = ["Red", "Yellow", "Green" /*, etc. */] },
{ Name = "Size", Values = ["S", "M", "L" /*, etc. */] },
{ Name = "Length", Values = ["34", "35", "36" /*, etc. */] },
/* and so on...(up to 5 total) */
];
And I can get that data with LINQ like so:
var myVariations = myProduct.Variations.ToList();
How can I go about mapping those variations into a structure like this (for the eBay Trading API):
var ebayVariations = [
{
Name = "Red-S-34",
Value = [
// yes, these are arrays with only one item
{ Name = "Color", Values = [{Value = "Red"}] },
{ Name = "Size", Values = [{Value = "S"}] },
{ Name = "Length", Values = [{Value = "34" }] }
]
},
/* etc for all possible combinations */
];
Obviously the fact that the Values array holds only one value is a bit strange; but with eBay's Trading API if I list multiple values in a single Variation (which is easy to do compared to this recursive stuff) it complains. So alternatively, if you are familiar with the eBay Trading API, how can I get this to work in an "optimal" fashion, in-line with the way eBay intended Variations to be listed (called via AddFixedPricedItem, if you care).
I don't know anything about the eBay Trading API, but here's an article on computing a Cartesian Product with LINQ (the very last step drops the recursion in favor of aggregation).
I've changed terminology insignificantly, but wrote clarifying comments.
public IEnumerable<Combination> GetCombinations(Variation[] variations, int variationIndex, IEnumerable<VariationPosition> aggregatedPositions)
{
// We should choose one position from every variation,
// so we couldn't produce combination till we reach end of array.
if (variationIndex < variations.Length)
{
// Pick current variation.
var currentVariation = variations[variationIndex];
// Every variation has list of possible positions (Color could be Green, Redm, Blue, etc.).
// So we should walk through all the positions
foreach (var val in currentVariation.Positions)
{
// Current position. Variation's name will be used during creating result Combination.
var position = new VariationPosition()
{
Name = currentVariation.Name,
Value = val
};
// Add position to already aggregated on upper levels of recursion positions.
var newPositions = aggregatedPositions.Concat(Enumerable.Repeat(position, 1));
// So we picked some variation position
// Let's go deeper.
var combinations = this.GetCombinations(variations, variationIndex + 1, newPositions );
// This piece of code allows us return combinations in iterator fashion.
foreach (var combination in combinations)
{
yield return combination;
}
}
}
else
{
// We reached end of variations array
// I mean we have one position of every variation.
// We concatenate name of positions in order to create string like "Red-S-34"
var name = aggregatedPositions.Aggregate("", (res, v) => res += v.Name);
// This code is a little bit naive, I'm too lazy to create proper infrastructure,
// But its mission is to create content for property Value of your ebayVariations item.
var value = aggregatedPositions
.Select(v => new { Name = v.Name, Values = new[] { new { Value = v.Value } } })
.ToArray();
// And we return completed combination.
yield return new Combination()
{
Name = name,
Value = value,
};
}
}
And usage:
var allCombinations = this.GetCombinations(inputVariations, 0, new VariationPosition[0]).ToArray();

Categories