Reputation: 19544
In my C# program, I am querying a webservice and getting a reply stream back in JSON that looks something like this:
{"val":12345.12},{"val":23456.23},{"val":34567.01},...
or, with possibly more than 1 value per reply object:
{"val1":12345.12,"val2":1},{"val1":23456.23,"val2":3},....
And I have the following code utilizing the Newtonsoft.Json library that parses the stream and performs some action on each parsed object, one at a time:
public void ParseReply<T>(StreamReader sr, Action<T> action)
{
using (var reader = new JsonTextReader(sr))
{
var ser = new JsonSerializer();
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
break;
action(ser.Deserialize<T>(reader));
}
}
}
So, in the case of the second result, I have the following code:
public class MyObject
{
public double val1;
public double val2;
}
and then:
myJson.ParseReply<MyObject>(sr, obj => ...);
works perfectly.
But, in the case of the first reply (1 value per object), if I try and use my method in the following way:
myJson.ParseReply<double>(sr, dbl => ...);
I get an error saying:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Double' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
I'm really lost / curious how I could update my code to be able to parse both of these correctly and I'm a bit lost on this error message. Any help would REALLY be appreciated!!
Upvotes: 0
Views: 3321
Reputation: 116669
If you change your static method to return an IEnumerable<T>
rather than taking an Action<T>
, you will be able to chain together Enumerable
and LINQ to JSON methods in a very concise and natural way. Thus if you have:
public static class JsonExtensions
{
public static IEnumerable<T> ParseArray<T>(this TextReader textReader)
{
using (var reader = new JsonTextReader(textReader))
{
bool inArray = false;
var ser = JsonSerializer.CreateDefault();
while (reader.Read())
{
if (reader.TokenType == JsonToken.Comment)
continue;
if (reader.TokenType == JsonToken.StartArray && !inArray)
{
inArray = true;
continue;
}
if (reader.TokenType == JsonToken.EndArray)
break;
yield return ser.Deserialize<T>(reader);
}
}
}
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
// This method should be present on JToken but is only present on JContainer.
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new[] { node };
}
public static bool IsNumeric(this JTokenType type) { return type == JTokenType.Integer || type == JTokenType.Float; }
public static bool IsNumeric(this JToken token) { return token == null ? false : token.Type.IsNumeric(); }
}
You can do:
var json = @"[{""val"":12345.12},{""val"":23456.23},{""val"":34567.01}]";
// Select all properties named "val" and transform their values to doubles.
foreach (var val in new StringReader(json).ParseArray<JToken>()
.Select(t => (double)t.SelectToken("val")))
{
Debug.WriteLine(val);
}
// Select all primitive numeric values
foreach (var val in new StringReader(json).ParseArray<JToken>()
.SelectMany(t => t.DescendantsAndSelf())
.Where(t => t.IsNumeric())
.Select(t => (double)t))
{
Debug.WriteLine(val);
}
Upvotes: 1