I have the following JSON:
[
{
"name": "codeURL",
"value": "abcd"
},
{
"name": "authURL",
"value": "fghi"
}
]
I created the following objects:
public class ConfigUrlModel {
[JsonProperty("name")]
public abstract string name { get; set; }
[JsonProperty("value")]
public abstract string value { get; set; }
}
public class ConfigUrlsModel {
[JsonProperty]
public List<ConfigUrlModel> ConfigUrls { get; set; }
}
I am deserializing with the following line:
resultObject = Newtonsoft.Json.JsonConvert.DeserializeObject<ConfigUrlsModel>(resultString);
ConfigUrlsModel result = resultObject as ConfigUrlsModel;
I am getting the following error:
Exception JsonSerializationException with no inner exception: Cannot deserialize JSON array into type 'Microsoft.Xbox.Samples.Video.Services.Jtv.Models.ConfigUrl.ConfigUrlsModel'.
Exception JsonSerializationException with no inner exception: Cannot deserialize JSON array into type 'Microsoft.Xbox.Samples.Video.Services.Jtv.Models.ConfigUrl.ConfigUrlsModel'.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract(Type objectType, JsonContract contract)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contr at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract(Type objectType, JsonContract contract)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contrNavigationService:OnNavigateToMessage PageSourceUri=/Microsoft.Xbox.Sample.UI;component/ErrorPrompt/ErrorPromptView.xaml
What am I doing wrong? How do I fix this?
The root JSON container is an array, not an object, so deserialize it thusly:
var configUrls = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ConfigUrlModel>>(resultString);
var result = new ConfigUrlsModel { ConfigUrls = configUrls }; // If you still need the root object.
A JSON array is an ordered list of values [value1, value2, ..., value], which is what is shown in your question. Json.NET will convert .NET arrays and collections to JSON arrays, so you need to deserialize to a collection type.
The JSON you are sending is an array, but you are attempting to deserialize it to an object. Ether change your JSON so it matches the object defintion at the top level, and has a matching attribute, like this:
{
"ConfigUrls":[
{
"name":"codeURL",
"value":"abcd"
},
{
"name":"authURL",
"value":"fghi"
}
]
}
Or change your deserialize call to:
var urls = DeserializeObject<List<ConfigUrlModel>>(json);
This will return a List<ConfigUrlModel> which you can either use directly, or wrap in a ConfigUrlModels instance if you need to.
Additionally it is possible to deserialize this JSON directly to the desired class, by creating a custom newtonsoft JsonConverter sublcass. But that's going to make the code a little less clear, so avoid it if possible.
Related
I have JSON which contains property within quotes “” here is an example
{
"data1" : "value",
“data2” : {
property : "property value"
}
}
And my dto:
public class Data
{
public string Data1 {get;set;}
public object Data2 {get;set;}
}
and deserialization code
static void Main(string[] args)
{
var jsonString = " { \"data1\" : \"value\",“data2” : {property: \"property value\"}} ";
var data = JsonConvert.DeserializeObject<Data>(jsonString);
}
On deserialization I'm getting an error:
'Invalid property identifier character: “. Path 'data1', line 1, position 23.'. Peace of stack trace at Newtonsoft.Json.JsonTextReader.ParseProperty()
at Newtonsoft.Json.JsonTextReader.ParseObject()
at Newtonsoft.Json.JsonTextReader.Read()
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject ....
I there any workaround on how to fix it, maybe there is a possibility to additionally add another property identifier?
Thanks in advance.
You JSON is just invalid (you can check that using a validator)
By replacing those fancy quotes it becomes valid so you should maybe consider applying a replace before parsing it?
jsonString = jsonString.Replace('“', '"');
But the best would be to fix the issue during the serialization because that JSON is invalid and will cause issue everywhere.
This question already has answers here:
How can I parse a JSON string that would cause illegal C# identifiers?
(3 answers)
Closed 3 years ago.
I am working on code that processes responses from OpenLibrary. This is a rest service that returns books based on a passed in ISBN. An example URL is this:
https://openlibrary.org/api/books?bibkeys=ISBN:9780596005405&jscmd=data&format=json
{
ISBN:9780596005405: {
publishers: [
{
name: "O'Reilly"
}
],
pagination: "xxxii, 854 p. :",
identifiers: {
lccn: [
"2006281089"
],
openlibrary: [
"OL17924716M"
],
isbn_10: [
"0596005407"
],
goodreads: [
"58129"
],
librarything: [
"187028"
],
},
.... other properties omitted for brevity
}
I have these objects:
public class OLResult
{
Publishers Publishers { get; set; }
// other properties
}
public class Publishers
{
// properties go here
}
I created C# objects that are identical to what OpenLibrary returns. However, if you look at the response, you'll notice it's a JSON with a root element with a weird key: ISBN: followed by the passed in ISBN number. Newtonsoft.Json does not know how to map this dynamic key to my OLResult object.
I have created a simple converter to manually convert this JSON to the correct object:
public class OpenLibraryResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
OLResult result = new OLResult();
// Perform magic to copy JSON results to result object.
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
I have also added an attribute [JsonConverter(typeof(OpenLibraryResultConverter))] to my OLResult class. When I call JsonConvert.DeserializeObject<OLResult>(jsonStringResult);, and use the debugger to check the ReadJson method, I can not seem to find the JSON data of the jsonStringResult parameter I passed in to the converter. Most properties of the parameters passed into the ReadJson method appear null or empty.
My question: how do I successfully read out the JSON string in my new JsonConverter?
In case you don't need that dynamic property number: ISBN:XXXXXX you can go with something simple like using partial JSON serialization that you can check out here
It's not clear from the post., but the first part is actually a label, so the overall structure is like this:
{
"ISBN:9780596005405": {...}
}
Note that the label contains a colon, which makes it more difficult to translate directly into a C# object.
But either way, you'll need to either define an object class for that and parse it, or as #Ernestas suggests, skip it.
I am looping over a folder of JSON files, and I am trying to pull out some information from them; however, I am finding it greatly difficult to do so.
I have started to build my objects to deserialize. I have a particular node I would normally deserialize as an object, but when it's empty, it presents as an empty array. Please see the definition field in the example JSON below:
{
"name": "Example",
"description": "Example JSON",
"properties": {
"foo": "bar",
"foo1": "bar2",
"foo3": "bar4"
},
"stages": {
"This is a stage": {
"stageInfo1": "blah",
"stageInfo2": "blah",
"integration": {
"x": "x",
"y": "y",
"z": "z",
"definition": []
}
},
"Another Stage": {
"stageInfo1": "blah",
"stageInfo2": "blah",
"integration": {
"x": "x",
"y": "y",
"z": "z",
"definition": {
"5a4d7de4c6518": {
"Editable": true,
"ID": "5a4d7de4c6518",
"Name": "My example"
}
}
}
}
}
}
As the definition name can change (in this case it is 5a4d7de4c6518), I thought a dictionary would be best, but an error is thrown when an empty array is presented.
[JsonProperty("definition")]
public Dictionary<string, Definition> definition;
Error:
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,JsonProcessReader.Models.Stages+IntegrationDefinition]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
An empty array is incompatible with a dictionary structure, which gives rise to the error you are seeing. Since it seems you cannot easily change the JSON, you will need to use a JsonConverter to handle this situation. Here is a generic one that should work for you:
class TolerantObjectConverter<T> : JsonConverter where T: new()
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
object result = new T();
if (token.Type == JTokenType.Object)
{
serializer.Populate(token.CreateReader(), result);
}
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
This converter works by temporarily loading the relevant piece of JSON into a JToken and then checking whether it is actually an object before trying to convert it. So if it is an array or some other token type that won't convert properly, it will return an empty T instance instead.
To use the converter, simply add a [JsonConverter] attribute to your dictionary property like this:
public class Integration
{
...
[JsonConverter(typeof(TolerantObjectConverter<Dictionary<string, Definition>>))]
public Dictionary<string, Definition> definition { get; set; }
}
Here is a working demo: https://dotnetfiddle.net/83dQoC
I want to deserialize JSON containing long decimal values into custom types to maintain their precision (i.e., a custom BigDecimal class). I'm using Json.NET 9.0.1 and .NET 4.6.1. I've tried using a JsonConverter, but it seems that the value available when ReadJson is called has already been identified and read by Json.NET as a .NET decimal type and is limited to its precision.
Ideally I would have access to the raw string so I could put it in a custom type. I can use string properties on the target object and it deserializes the full string, but then I'd have to further process the object (i.e., copy it into another representation) and that's especially messy across a large schema.
Any thoughts on a better approach?
Here's the target class:
public class DecimalTest
{
public string stringValue { get; set; }
public decimal decimalValue { get; set; }
public BigDecimal bigDecimalValue { get; set; }
}
Here's a test with JSON:
[TestMethod]
public void ReadBigDecimal_Test()
{
var json = #"{
""stringValue"" : 0.0050000012852251529693603515625,
""decimalValue"" : 0.0050000012852251529693603515625,
""bigDecimalValue"" : 0.0050000012852251529693603515625
}";
var settings = new JsonSerializerSettings();
settings.FloatParseHandling = FloatParseHandling.Decimal;
settings.Converters.Add(new JsonBigDecimalConverter());
var result = JsonConvert.DeserializeObject<DecimalTest>(json, settings);
Assert.IsNotNull(result);
Assert.AreEqual("0.0050000012852251529693603515625", result.stringValue);
Assert.AreEqual(0.0050000012852251529693603516m, result.decimalValue);
// *** This case fails ***
Assert.AreEqual("0.0050000012852251529693603515625", result.bigDecimalValue.ToString());
}
Here's the custom converter:
public class JsonBigDecimalConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(BigDecimal));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// *** reader.Value here already appears to be a .NET decimal.
// *** If I had access to the raw string I could get this to work.
return BigDecimal.Parse(reader.Value.ToString());
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Could you try if the following implementation of ReadJson works as you expect:
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return BigDecimal.Parse(token.ToString());
}
Update
Unfortunately the above won't work. There seems to be no way to read the raw string from the JSON data.
Also note that in my tests the assert for stringValue fails first. See this working example: https://dotnetfiddle.net/s0pqg3
I assume this is because Json.NET internally immediately parses any number token it encounters according to the specified FloatParseHandling. The raw data is never preserved.
I think the only solution is to wrap the big decimal string in quotes like so:
"bigDecimalValue" : "0.0050000012852251529693603515625"
Here is a working example that does exactly that in order to preserve the desired precision: https://dotnetfiddle.net/U1UG3z
I have troubles deserializing my object. It contains following property:
public List<IShape> Shapes { get; set; };
and JSON.NET deserializer always tells me, that it is not possible to instantiate an interface.
I have several classes which implement interfaces which implement IShape interface, e.g. Polyline -> IPolyline -> IShape. I tried two solutions already:
https://stackoverflow.com/a/8031283/1525505
https://stackoverflow.com/a/12769061/1525505
But I got the same exception, that IShape cannot be instantied, was thrown.
I serialize the object with TypeNameHandling.Auto, TypeNameHandling.All doesn't help too, even when I use the converters mentioned in posts I linked above.
Does anyone know of a solution to this problem? If some code is needed I will gladly post it.
Here is a sample of JSON that is generated.
"$type": "SketchModel.Layer, SketchModel",
"Id": 57865477,
"Shapes": {
"$type": "System.Collections.Generic.List`1[[SketchModel.Shapes.AbstractShapes.IShape, SketchModel]], mscorlib",
"$values": [
{
"$type": "SketchModel.Shapes.Polyline, SketchModel",
This line is responsible for the problem:
"System.Collections.Generic.List`1[[SketchModel.Shapes.AbstractShapes.IShape, SketchModel]], mscorlib"
It simply doesn't know how to instantiate IShape. If I create a custom converter and let it return a Polyline for each IShape, it works, but doesn't create any other shapes (e.g. Ellipses).
In the
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) method which is overriden in the custom converter, if I let me print the full type name of objectType it's always IShape, never anything else...
The exception makes sense because the deserializer doesn't know what concrete type the interface is supposed to represent when hydrating.
During serialization, JSON.NET allows you to configure it to add some meta data to be used in this case. This SO question has an answer explaining how to configure it.
The configuration will add a type property to the JSON that will be used during deserialization.
I had exactly this problem and only solved it by explicitly providing a converter for the Type. It doesn't work if I use annotations - I need to pass the converters in at deserialization - basically like this:
state = JsonConvert.DeserializeObject<StateImpl>((String)stateObject, new JsonConverter[] { new StatePersistenceStateEntryConverter(), new StatePersistenceUserInteractionConverter() });
Where my StateImpl object included these properties:
[DataMember]
[JsonProperty("stateEntries", TypeNameHandling = TypeNameHandling.Auto)]
public List<IStateEntry> StateEntries
{
get;
set;
}
[DataMember]
[JsonProperty("precommitStateEntry", TypeNameHandling = TypeNameHandling.Auto)]
public IPrecommitStateEntry PrecommitStateEntry
{
get;
set;
}
IPrecommitStateEntry extends the IStateEntry interface (just FYI in case you're wondering why the extra logic in the converter).
Also - in my IStateEntry object, I have a similar child problem:
[DataMember]
[JsonProperty("userInteractions", TypeNameHandling = TypeNameHandling.Auto)]
public List<IUserInteraction> UserInteractions
{
get;
set;
}
So my object has child list properties of IStateEntry and an IStateEntry has a further child list of IUserInteraction. My converters are as follows:
public class StatePersistenceStateEntryConverter : CustomCreationConverter<IStateEntry>
{
public override IStateEntry Create(Type objectType)
{
if (objectType == typeof(IPrecommitStateEntry))
{
return new PrecommitStateEntry();
}
else
{
return new StateEntry();
}
}
}
And...
public class StatePersistenceUserInteractionConverter : CustomCreationConverter<IUserInteraction>
{
public override IUserInteraction Create(Type objectType)
{
return new UserInteraction();
}
}
Literally all they're doing is creating an instance of that particular object implementation.
So I don't know why the converters are needed as clearly a List can be instantiated - just not the IStateEntry individually. Clearly there's a bug in the NewtonSoft implementation somewhere - or I'm missing something fundamental.
Hope that helps. It was a frustrating few hours for me, but now working!
Without including concrete types within your Json string, you can use a JsonConverter to convert an IList<SomeInterface> to their concrete type:
Class to deserialize:
public partial class MovieInfo : IMovieInfo
{
~~~~
[JsonProperty("genres")]
[JsonConverter(typeof(ListConverter<IGenre, Genre>))]
public IList<IGenre> Genres { get; set; }
~~~~
}
JsonConverter example:
public class ListConverter<I, T> : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(I);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
JArray jsonArray = JArray.Load(reader);
var deserialized = (List<T>)Activator.CreateInstance(typeof(List<T>));
serializer.Populate(jsonArray.CreateReader(), deserialized);
return deserialized as IList<I>;
}
}