I'm trying to serialize some BSON returned from MongoDB into JSON:
var bson = MongoDB.Bson.BsonDocument.Parse(#"{
""_id"": ObjectId(""5a45a64ec7fe121dfc673c6f""),
""MyOtherId"": ObjectId(""5a45a64dc7fe121dfc673c6e""),
""Blah"": ""Test""
}");
And I want to morph the ObjectID into a string so I get
"_id": "5a45a64ec7fe121dfc673c6f",
instead of
"_id": {"$oid": "5a4597a3d999f209e05df993"},
so I tried this:
var result = Newtonsoft.Json.JsonConvert.SerializeObject(bson, new Newtonsoft.Json.Bson.Converters.BsonDataObjectIdConverter());
But I get an error:
Newtonsoft.Json.JsonSerializationException: 'Error getting value from 'AsBoolean' on 'MongoDB.Bson.BsonObjectId'.'
Inner exception:
InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonObjectId' to type 'MongoDB.Bson.BsonBoolean'.
I still get the error with the simplified version:
var result = Newtonsoft.Json.JsonConvert.SerializeObject(bson);
using Newtonsoft.Json v10.0.3 and MongoDB.Driver v2.5.0
Newtonsoft tries to read every property of an object into a Json value.
So if your object has public string Lol {get { throw new Exception}}, Newtonsoft will try to read Lol, get the exception, and will fail serializing.
Now, a Bson value has lots of properties like AsBoolean, AsString, which throw an exception if the actual value isn't what they expect. Mongo expects you to know what value is contained in the bson and access the appropriate proeprty. But since Newtonsoft tries to access all properties, you get that error.
You can work around that issue by writing a custom converter for BsonValue and passing it to Newtonsoft in the SerializationSettings. Your converter will need to call getBsonType() on the VsonValue, and then the correct AsBoolean, AsInt or whatever to get the actual value.
Related
My HTTP response data includes a field that is structured as a Dictionary<string,object>. Accessing the Object in debug mode shows me what I expect to see, i.e.
ValueKind = Object:
{ "someProp" : "someValue",
"anotherProp" : "anotherValue" .. }
=> the object therefore contains the large json formatted data that I need to access. However, I can't figure out how to do that .. The commonly suggested method returns null:
var myValue = Odata.GetType().GetProperty("someProp").GetValue(Odata, null);
which makes me think there is something I should be serializing which I am not and regardless how much I tried, I can't find any methods to work.
Here is some more info, if it is not enough I will add whatever is needed. I broke it down a lot to try to show clearly what I am getting each step):
// assume Odata = the Object value from the dictionary
//perform reflection
var type = Odata.GetType() // this gives System.Text.json.JsonElement
//get type properties at runtime
var props = type.GetProperties()
//returns array of 2 :
//System.Text.Json.ValueKind and System.Text.json.JsonElement
//attempting to access my properties in the JsonElement
var someValue= type.GetProperty("someProp").GetValue(Odata,null) //this gives null
I don't necessarily want an exact solution, but pointing me to the right place to read would also be very useful!
When you do GetType().GetProperty() you're using the GetProperty() method from Type class, you're using reflection. You want to use the GetProperty method of JsonElement class instead:
var myValue = Odata.GetProperty("someProp").GetString();
I am getting intermittent error when executing my .NET C# code. Below is the error.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Newtonsoft.Json.Linq.JValue' does not contain a definition for 'value'
Below is the code I am using -
dynamic obj = JsonConvert.DeserializeObject(response);
string value = obj.responseObject.value.token;
I am getting this error when I try to execute the above code -
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Newtonsoft.Json.Linq.JValue' does not contain a definition for 'value'
Please, any help is appreciated. Thank you.
Your problem is that you are attempting to deserialize JSON that usually looks like the following:
{"responseObject" : { "value" : { "token" : "some token" } } }
By using Json.NET's dynamic runtime binding to JSON properties:
dynamic obj = JsonConvert.DeserializeObject(response);
string value = obj.responseObject.value.token;
In cases where your code works, the JSON contains the properties you expect with the types you expect. But when your JSON fails to confirm to your expected schema -- specifically the value of "value" is an atomic JSON value or null rather than a nested object -- then Newtonsoft's dynamic binding code will fail with the error you see. E.g. given the following two JSON samples:
{"responseObject" : { "value" : "some atomic value such as an error message" } }
{"responseObject" : { "value" : null } }
then obj.responseObject.value.token will throw a 'Newtonsoft.Json.Linq.JValue' does not contain a definition for 'token' exception.
Demo fiddle #1 here.
So, what are your options for a workaround?
dynamic binding has a convenient syntax which can look enticing, however when you use dynamic you lose all compile-time error checking, and often find yourself with completely inscrutable errors when your objects are not exactly as you expect. Since Json.NET's dynamic deserialization actually deserializes to a LINQ-to-JSON JToken hierarchy, you should do so explicitly:
var obj = JsonConvert.DeserializeObject<JObject>(response);
And now to access obj.responseObject.value.token in a fault-tolerant manner, your options include:
Use SelectToken():
var value = (string)obj.SelectToken("responseObject.value.token");
SelectToken() is more fault-tolerant than dynamic binding and will simply return null if no token exists at the specified path.
Use the JTtoken indexer and explicitly check that obj["responseObject"]?["value"] is a JSON object:
var value = (string)(obj["responseObject"]?["value"] as JObject)?["token"];
As a variation of #2, you could introduce some extension methods as follows:
public static partial class JsonExtensions
{
public static JObject AsJObject(this JToken #in) => #in.As<JObject>();
public static TJToken As<TJToken>(this JToken #in) where TJToken : JToken => #in as TJToken;
}
and do:
var value = (string)obj["responseObject"].AsJObject()?["value"].AsJObject()?["token"];
You could use Json.NET Schema to validate your JSON before parsing. Note however that Json.NET Schema is not free.
Demo fiddle #2 demonstrating options 1-3 here.
I would like to read a JSON string via the Newtonsoft Json library. It works fine for any basic datatype, but it does not work for a List<double> or any List for that matter.
The test application looks like the following:
static void main()
{
string jsonString = #"
{
'name': 'set1',
'Xvv': {
'parameter': 'hByT',
'values': '[1,2,3]'
}
}";
JObject Json = JObject.Parse(jsonString);
var name = Json["name"].ToString();
var data = Json["Xvv"]["values"].Value<List<double> >(); // Raises error
}
The last line throws the following exception:
System.InvalidCastException: Invalid cast from 'System.String' to 'System.Collections.Generic.List
Is there a way to access the data directly as a List<double>?
In the example JSON you've provided, values is a string. A proper JSON array would be
'values': [1,2,3]
Anyway, after changing the string to the array, .Value<List<double>>() would throw an exception, that a JArray cannot be cast to a JToken - unfortunately I do not really know, why it does not work.
However, JToken.ToObject<T> does the trick, it
Creates an instance of the specified .NET type from the JToken
(see the documentation for ToObject)
With the line
var data = Json["Xvv"]["values"].ToObject<List<double>>();
you can cast the array correctly.
If an IEnumerable would be fine for you, too, you could also use
var data = Json["Xvv"]["values"].Values<double>();
I am trying to get all the images from an imgur album. It works fine with all of the albums, except when i try to get images from
this album
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[Imgur.API.Models.Impl.Image]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) 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) 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.
Here is my code, which throws out the exception.
public async Task<List<IImage>> dosomething()
{
IEnumerable<IImage> image = await Task.Run(AccessTheWebAsync);
return image.ToList(); ;
}
async Task<IEnumerable<IImage>> AccessTheWebAsync()
{
var client = new ImgurClient(token);
var endpoint = new AlbumEndpoint(client);
IEnumerable<IImage> images = await endpoint.GetAlbumImagesAsync(albumlink);
return images;
}
The value for albumlink that you're passing is incorrect, it should not have the #0 on the end.
The value for albumlink should just be Dc2k6.
I'm not sure why the #0 is there, I assume it can select a specific image or something but I couldn't get it to work. To prove it's irrelevant, add any other value after the #:
https://imgur.com/a/Dc2k6#SAUSAGES
I'm trying to do is deserialize json into an object in c#. What I want to be able to do is pass any object get it's type and deserialize the json into that particular object using the JSON.Net library. Here are the lines of code.
Object someObject1 = someObject;
string result = await content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<someObject1.GetType()>(result);
The last line throws an exception of
operator '<' cannot be applied to operands of type 'method group'
How do I get the data type in the <> without c# complaining. What do I have to do to make this code work? And what knowledge am I missing?
JsonConvert.DeserializeObject<T> needs a compile-time type. You can't pass it a type in run time as you want to do in question (nothing different than declaring a List<T>). You should either deserialize to a generic json object JObject (or to dynamic) or you should create an instance of an object and fill it with json.
You can use the static method PopulateObject (of course if your object's properties match the json you want to deserialize).
JsonConvert.PopulateObject(result, someObject1 );
You can ignore the generic method and use dynamic:
var myObj = (dynamic)JsonConvert.DeserializeObject(result);
However, if the objects aren't of the same type you'll have a hard time distinguishing between the types and probably hit runtime errors.
For anyone bumping into this problem, the newer versions of Newtonsoft JSON have an overload that takes a type as a second argument and where you can pass a dynamic value without jumping through any hoops:
var myObj = JsonConvert.DeserializeObject(string serializedObject, Type deserializedType);
This is the best way to populate an object's fields given JSON data.
This code belongs in the object itself as a method.
public void PopulateFields(string jsonData)
{
var jsonGraph = JObject.Parse(jsonData);
foreach (var prop in this.GetType().GetProperties())
{
try
{
prop.SetValue(this, fields[prop.Name].ToObject(prop.PropertyType), null);
}
catch (Exception e)
{
// deal with the fact that the given
// json does not contain that property
}
}