I have in following string:
[{"key":"cod1","value":["RR4","RR6"]},{"key":"cod2","value":["RR2","RR3"]},{"key":"cod3","value":["RR1","RR2"]}]
and I want to save it in a dictionary like this:
Dictionary<string, List>
The project is made in C# and to try to do it I use the following statement:
Dictionary<string, List<string>> dic = new Dictionary<string, List<string>>();
dic = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(ss);
But I get this error:
"Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary2[System.String,System.Collections.Generic.List1[System.String]]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.\r\nTo 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.\r\nPath '', line 1, position 1.\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)\r\n at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)\r\n at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)\r\n at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)\r\n at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)\r\n at operationWebAPI.Repository.TransferLadleRepository.LastEvents() in C:\Users\raulm\Documentos\Proyectos\SOSteelShop\Backend\operationWebAPI\Repository\TransferLadleRepository.cs:line 1164\r\n at operationWebAPI.Controllers.TransferLadleController.GetLastEvents() in C:\Users\raulm\Documentos\Proyectos\SOSteelShop\Backend\operationWebAPI\Controllers\TransferLadleController.cs:line 229"
I can't find how to solve the problem. Can you help me? Thanks a lot!!
Your root json element is array, so the data can be represented as collection of Dictionary<string, JToken>:
var result = JsonConvert.DeserializeObject<List<Dictionary<string, JToken>>>(json);
But I suggest to introduce a class representing data and processing it correspondingly:
public class Root
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("value")]
public List<string> Value { get; set; }
}
Root myDeserializedClass = JsonConvert.DeserializeObject<List<Root>>(json);
JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array
Not sure if it will work but you can try that
JsonArrayAttrribute Usage in C# Code ( Json.Net )
you can extract data as a dictionary
Dictionary<string,List<string>> dic = JArray.Parse(json)
.ToDictionary(k=> (string) k["key"], k=> k["value"].ToObject<List<string>>());
but since you have a collection , it is more natural to extract the data as a collecton
List<KeyValuePair<string,List<string>>> list = JsonConvert.DeserializeObject<List<KeyValuePair<string,List<string>>>>(json);
//how to use
List<string> cod2 = list.Where(l =>l.Key=="cod2").FirstOrDefault().Value;
Related
I'm trying to use Newtonsoft.Json to serialize and deserialize a Dictionary<(int, int), MyClass>. Because (int, int) has to get serialized to a string, I have to provide a custom TypeConverter to deserialize it back to a tuple:
public class Tuple2Converter<T1, T2> : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var parts = Convert.ToString(value).Trim('(').Trim(')').Split(", ");
var item1 = (T1)TypeDescriptor.GetConverter(typeof(T1)).ConvertFromInvariantString(parts[0])!;
var item2 = (T2)TypeDescriptor.GetConverter(typeof(T2)).ConvertFromInvariantString(parts[1])!;
return (item1, item2);
}
}
// ...
TypeDescriptor.AddAttributes(typeof((int, int)), new TypeConverterAttribute(typeof(Tuple2Converter<int, int>)));
var resultingObject =
JsonConvert.DeserializeObject<Dictionary<(int Q, int R), HexWithMeta>>(dictString);
However, when deserializing I now get the error:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.ValueTuple`2[System.Int32,System.Int32]' because the type requires a JSON string value to deserialize correctly.
To fix this error either change the JSON to a JSON string value 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.
It's trying to use the custom TypeConverter to convert another (int, int) to a C# tuple, but this time, the tuple was serialized in a standard way (JSON object instead of string), because this tuple is one that exists on MyClass, so it was serialized like:
"QR": {
"Item1": 0,
"Item2": 0
}
How can I get Newtonsoft.Json to use the custom TypeConverter when deserializing the string-encoded tuple on the Dictionary key, but not for any tuples contained within the Dictionary's serialized values?
Note that I am only globally binding my TypeConverter via TypeDescriptor.AddAttributes() to get correct JSON serialization, I don't need to do it for other reasons.
Even using a custom contract resolver, Json.NET doesn't have a convenient way to inject a custom method to convert dictionary keys from and to JSON property names.[1] Binding in a global TypeConverter is the documented way to bind JSON property names to a complex dictionary key type. But, as you have found, binding in a custom TypeConverter for a complex type will also cause that type to be serialized as a string when serialized standalone as well as when formatted as a dictionary key.
Thus if you want to use a custom string conversion logic for a specific dictionary key type (here ValueTuple<int, int>) but not use the same logic when serializing that type standalone, you have the following options:
You could bind a TypeConverter globally as you are currently doing, then cancel use of the TypeConverter via a custom JsonConverter or contract resolver as shown in this answer to Newtonsoft.JSON cannot convert model with TypeConverter attribute.
You could skip using the TypeConverter and create a custom JsonConverter for your Dictionary<(int, int), MyClass>.
Since binding a TypeConverter globally via TypeDescriptor.AddAttributes might have unexpected side-effects, and you don't particularly want to do it anyway, I recommend the custom JsonConverter approach. Here is one that works:
public class DictionaryTupleConverter<T1, T2, TValue> : JsonConverter<Dictionary<(T1, T2), TValue>>
{
readonly TypeConverter converter1 = TypeDescriptor.GetConverter(typeof(T1)); // Cache for performance
readonly TypeConverter converter2 = TypeDescriptor.GetConverter(typeof(T2));
public override Dictionary<(T1, T2), TValue>? ReadJson(JsonReader reader, Type objectType, Dictionary<(T1, T2), TValue>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var innerDictionary = serializer.Deserialize<Dictionary<string, TValue>>(reader);
if (innerDictionary == null)
return null;
var dictionary = existingValue ?? (Dictionary<(T1, T2), TValue>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator!();
foreach (var pair in innerDictionary)
dictionary.Add(ConvertFrom(pair.Key), pair.Value);
return dictionary;
}
public override void WriteJson(JsonWriter writer, Dictionary<(T1, T2), TValue>? value, JsonSerializer serializer) =>
serializer.Serialize(writer, value!.ToDictionary(p => ConvertTo(p.Key)!, p => p.Value));
(T1, T2) ConvertFrom(string value)
{
var parts = value.Trim('(').Trim(')').Split(",");
var item1 = (T1)converter1.ConvertFromInvariantString(parts[0].Trim())!;
var item2 = (T2)converter2.ConvertFromInvariantString(parts[1].Trim())!;
return (item1, item2);
}
string ConvertTo((T1, T2) value)
{
var s1 = converter1.ConvertToInvariantString(value.Item1)!;
var s2 = converter2.ConvertToInvariantString(value.Item2)!;
return string.Format("({0},{1})", s1, s2);
}
}
Then serialize and deserialize using the following settings:
var settings = new JsonSerializerSettings
{
Converters = { new DictionaryTupleConverter<int, int, HexWithMeta>() },
};
Notes:
I am unsure whether ValueTuple<T1, T2>.ToString() is locale-invariant (this github issue suggests not), so I recommend creating your own method to convert your tuples to strings that is verifiably locale-invariant.
Your conversion methods for (T1, T2) from and to a string do not take into account the possibility that the inner strings may contain commas or parentheses. You may want to enhance the parsing and formatting logic with some sort of simple CSV parser.
Demo fiddle here.
[1] For confirmation you may check the source code for JsonSerializerInternalWriter.GetPropertyName(), the method used to generate a JSON property name from a dictionary key. The method is not virtual and is implemented as a series of hardcoded checks with no option to inject custom logic.
I have the error in the title when a try to call webApi.
This is my class:
public class NotificationDTO
{
public long idCompanyService { get; set; }
public DocTypeEnum DocumentType { get; set; }
public List<Tuple<string, byte[], System.Net.Mime.ContentType>> Attachments { get; set; }
}
This in my object:
NotificationDTO notifDto = new NotificationDTO()
{
idCompanyService = idCompServ,
DocumentType = DocType,
Attachments = listAttac
};
And this is the call:
HttpContent content = new StringContent(JsonConvert.SerializeObject(notifDto), Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(uri, content).Result;
This is a Json:
{"idCompanyService":1234,"DocumentType":0,"Attachments":[{"Item1":"test.pdf","Item2":"ThisIsABase64Data","Item3":{"Boundary":null,"CharSet":null,"MediaType":"application/pdf","Name":null,"Parameters":[]}}}
Attachments have the problem because is a List<Tuple<string, byte[], ContentType>>.
Error message:
Exception: Newtonsoft.Json.JsonSerializationException: Cannot populate list type System.Net.TrackingStringDictionary. Path 'Attachments.$values[0].Item3.Parameters.$values', line 1, position 105257.
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
in Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
Object Example
Try example:
You can reproduce the error with a simple web api controller:
[HttpPost]
[Route("api/TestNotification")]
public IHttpActionResult TestNotification(NotificationDTO dto)
{
return Ok();
}
And a Json like this in a Post Caller:
{"idCompanyService":1234,"DocumentType":1,"Attachments":[{"Item1":"wqere.pdf","Item2":"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=","Item3":{"Boundary":null,"CharSet":null,"MediaType":"application/pdf","Name":null,"Parameters":[]}},{"Item1":"ewqeqwe.xml","Item2":"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=","Item3":{"Boundary":null,"CharSet":null,"MediaType":"text/xml","Name":null,"Parameters":[]}}]}
Finally, a .Net fiddle that demonstrates the failure when deserializing the above JSON string to a NotificationDTO directly, via:
var dto = JsonConvert.DeserializeObject<NotificationDTO>(json);
can be found here: https://dotnetfiddle.net/EaPhm9.
Your real problem here is that your DTO refers to objects of type System.Net.Mime.ContentType which in turn has a member ContentType.Parameters which is of declared type System.Collections.Specialized.StringDictionary (and actual type System.Net.TrackingStringDictionary).
Unfortunately this ancient type (from .Net 1.1) doesn't even implement IDictionary. Since it only implements the untyped enumerator System.Collections.IEnumerable, Json.NET has no way to know how to deserialize it, and throws the exception you are seeing when asked to do so.
Thus it will be necessary to write a custom JsonConverter to deserialize objects of or derived from this type:
using System.Collections.Specialized;
public class StringDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(StringDictionary).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var tokenType = reader.SkipComments().TokenType;
if (tokenType == JsonToken.Null)
return null;
var dictionary = existingValue as StringDictionary ?? (StringDictionary)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
switch (tokenType)
{
case JsonToken.StartArray:
{
// StringDictionary serialized without converter
var list = serializer.Deserialize<List<KeyValuePair<string, string>>>(reader);
foreach (var pair in list)
dictionary.Add(pair.Key, pair.Value);
}
break;
case JsonToken.StartObject:
{
// StringDictionary serialized with converter
var typedDictionary = serializer.Deserialize<Dictionary<string, string>>(reader);
foreach (var pair in typedDictionary)
dictionary.Add(pair.Key, pair.Value);
}
break;
default:
throw new JsonSerializationException(string.Format("Unknown token {0} at path {1}", tokenType, reader.Path));
}
return dictionary;
}
// Change to false if you want the dictionary written as an array of key/value objects.
public override bool CanWrite { get { return true; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dictionary = (StringDictionary)value;
writer.WriteStartObject();
foreach (DictionaryEntry entry in dictionary)
{
writer.WritePropertyName((string)entry.Key);
writer.WriteValue((string)entry.Value);
}
writer.WriteEndObject();
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
}
Once written, you will need to use the converter on the server side to deserialize the incoming JSON. You don't specify which version of Web API you are using. To add a converter globally, see
ASP.NET Web API 2: See How to set custom JsonSerializerSettings for Json.NET in MVC 4 Web API? and also the second part of this answer to Registering a custom JsonConverter globally in Json.Net. In this scenario your code would look something like:
protected void Application_Start()
{
var config = GlobalConfiguration.Configuration;
var settings = config.Formatters.JsonFormatter.SerializerSettings;
settings.Converters.Add(new StringDictionaryConverter());
}
ASP.NET Core MVC: see JsonSerializerSettings and Asp.Net Core or Setting JsonConvert.DefaultSettings asp net core 2.0 not working as expected. Here again you would access the global JsonSerializerSettings and add an StringDictionaryConverter as shown above.
You may also want to use the converter on the client side as well to serialize your JSON. If you do, your content parameters will be serialized more compactly as a JSON object, rather than as a key/value array. (The converter accepts both forms as input.)
Sample fiddle here.
I've got a service that takes various file formats from my clients and converts them to XML to be processed by an internal processing engine. For JSON I'm using Json.Net's JsonConvert.DeserializeXmlNode to convert the JSON to XML. Today I've run into an issue where a client is sending JSON that contains a property with an empty name, which apparently is a valid scenario. When I try to convert to XML I get the exception below. What I'm trying to figure out is the best way to tell the XmlNodeConverter that I want it to either skip or give a default name to an element when this happens. Is that possible?
Any advice would be greatly appreciated! Thanks!
Sample Unit Test:
[Test]
public void TestParseEmptyName( )
{
string json = #"
{
""Row"":{
"""":123
}
}";
XmlDocument xdoc = JsonConvert.DeserializeXmlNode( json );
}
The exception I'm getting is:
Newtonsoft.Json.JsonSerializationException occurred
HResult=0x80131500
Message=XmlNodeConverter cannot convert JSON with an empty property name to XML. Path 'Row.', line 1, position 14.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Converters.XmlNodeConverter.ReadElement(JsonReader reader, IXmlDocument document, IXmlNode currentNode, String propertyName, XmlNamespaceManager manager)
at Newtonsoft.Json.Converters.XmlNodeConverter.DeserializeNode(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, IXmlNode currentNode)
at Newtonsoft.Json.Converters.XmlNodeConverter.CreateElement(JsonReader reader, IXmlDocument document, IXmlNode currentNode, String elementName, XmlNamespaceManager manager, String elementPrefix, Dictionary`2 attributeNameValues)
at Newtonsoft.Json.Converters.XmlNodeConverter.ReadElement(JsonReader reader, IXmlDocument document, IXmlNode currentNode, String propertyName, XmlNamespaceManager manager)
at Newtonsoft.Json.Converters.XmlNodeConverter.DeserializeNode(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, IXmlNode currentNode)
at Newtonsoft.Json.Converters.XmlNodeConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeXmlNode(String value, String deserializeRootElementName, Boolean writeArrayAttribute)
at Newtonsoft.Json.JsonConvert.DeserializeXmlNode(String value)
at BMS.Common.Test.Text.JSONParserFixture.TestParseEmptyName() in C:\SVNRepository\BMSProducts\LoanTradeDesk\trunk\Source Code\Common\BMS.Common.Test\Text\JSONParserFixture.cs:line 33
XmlNodeConverter does not seem to provide any public options to change how it handles empty JSON property names. I would have suggested subclassing the converter, but most of its internal methods are private, so that pretty much kills that idea.
It looks like your best option is to pre-process the JSON to replace the empty property names with some other value. You can use Json.Net's LINQ-to-JSON API to do this, then send the corrected JSON to JsonConvert.DeserializeXmlNode as you were before. I would create a small helper class like this:
public class JsonHelper
{
public static XmlDocument ConvertToXml(string json)
{
JToken token = JToken.Parse(json);
// Change "blank" to whatever you want the replacement name to be.
ReplaceEmptyPropertyNames(token, "blank");
return JsonConvert.DeserializeXmlNode(token.ToString());
}
public static void ReplaceEmptyPropertyNames(JToken token, string replaceWithName)
{
if (token.Type == JTokenType.Object)
{
foreach (JProperty prop in token.Children<JProperty>().ToList())
{
if (prop.Name == string.Empty)
{
prop.AddAfterSelf(new JProperty(replaceWithName, prop.Value));
prop.Remove();
}
ReplaceEmptyPropertyNames(prop.Value, replaceWithName);
}
}
else if (token.Type == JTokenType.Array)
{
foreach (JToken child in token.Children())
{
ReplaceEmptyPropertyNames(child, replaceWithName);
}
}
}
}
Then, in your code, just replace JsonConvert.DeserializeXmlNode() with JsonHelper.ConvertToXml():
[Test]
public void TestParseEmptyName( )
{
string json = #"
{
""Row"":{
"""":123
}
}";
XmlDocument xdoc = JsonHelper.ConvertToXml( json );
}
Fiddle: https://dotnetfiddle.net/ahvytn
I am parsing a large data file with JSON elements in C#. Deserialization fails for some strings with the error below.
Exception = Invalid cast from 'DateTime' to 'Int64'.
Message = Error converting value 3/28/2172 12:00:00 AM to type 'System.Int64'. Path 'ProductDetail.productId', line 1, position 4003."
The error doesn't occur for all strings, but seems to suggest the value is being interpreted as a DateTime value; although, it's defined as a long within the associated class.
Can I force the value to be interpreted as a long?
ProductDetail.productId is defined as a member of the following type:
public class ProductDetail
{
public string accreditedInvestorInd { get; set; }
public string accrueAcreteInd { get; set; }
public DateTime issueDate { get; set; }
// ... truncated for simplicity
public long productId { get; set; }
// ... truncated for simplicity
public string wkndHolPayInd { get; set; }
public string zeroCouponInd { get; set; }
}
I tried unsuccessfully to use a converter attribute with the hope of forcing a different interpretation. (Not sure if I'm using it correctly)...
[JsonConverter(typeof(string))]
public long productId { get; set; }
Also, I am using a custom DateTime format.
Here is a subset of the large data file I am trying to deserialize that corresponds to ProductDetail:
{"accrueAcreteInd":"Y","callableInd":"Y","calledInd":"N","cpnCurrency":"USD","cpnPmtFreqCode":"S","cpnRate":"2.8","curAmtOutstanding":"7825000.0","currencyCode":"USD","cusip":"3130H0AX1","datedDate":"20160303","dayCountMonthCode":"30","dayCountYearCode":"360","daysToSettle":"1","dtcEligibleInd":"Y","eomPmtInd":"N","exchange":"TRACE","fatcaCode":"1","firstCpnPmtDate":"20160901","hqlaCode":"2A","identifier":"3130H0AX1","identifierType":"cusip","inDefaultInd":"N","infoSourceCode":"R21RB","intAccrued":".91777778","intTypeCode":"FIX","is144aInd":"N","issueCountry":"US","issueDate":"20160303","issuePrice":"100.0","issuerIndustryCode":"11","issuerName":"FARMER MAC","issuerTicker":"FAMCA","lastCpnPmtDate":"20250901","lastUpdateTime":"20160830","lastUpdateTimeExt":"00:06:58","longDesc":"FAMCA 2.8 03/01/2026","maturityDate":"20260301","minDenom":"1000.0","minParAmount":"1000.0","mktSectorDes":"Govt","mtnInd":"Y","nextCallDate":"20170301","nextCallPrice":"100.0","nextCpnPmtDate":"20170301","origIssueAmt":"7825000.0","prevCpnPmtDate":"20160901","primaryIssInd":"Y","privatePlacementInd":"N","productId":"21720328","productTypeCode":"FRMNT","putableInd":"N","pxCleanInd":"Y","redempVal":"100.0","regSInd":"N","restrictedInd":"N","secHoldInd":"N","securityType":"FAMCCL","series":"NOTZ","shortDesc":"FAMCA 2.8 03/26","sinkableInd":"N","traceEligibleInd":"N","truePutInd":"N","wkndHolPayInd":"N","zeroCouponInd":"N"}
And here is how I am trying to deserialize the subset, where Line is the JSON string above:
var dateFormatSettings = new JsonSerializerSettings { DateFormatString = "yyyyMMdd" };
var detail = JsonConvert.DeserializeObject<ProductDetail>(Line, dateFormatSettings);
And here is the full exception for the simplified JSON:
Newtonsoft.Json.JsonSerializationException: Error converting value 3/28/2172 12:00:00 AM to type 'System.Int64'. Path 'productId', line 1, position 1117. ---> System.InvalidCastException: Invalid cast from 'DateTime' to 'Int64'.
at System.DateTime.System.IConvertible.ToInt64(IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
--- End of inner exception stack trace ---
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
The problem is related to the specific DateFormatString you are using:
var dateFormatSettings = new JsonSerializerSettings { DateFormatString = "yyyyMMdd" };
JSON has no literal syntax for dates and so, at a low level, Json.NET does some pattern matching to recognize strings that are dates, using the DateFormatString setting. Unfortunately your DateFormatString setting of yyyyMMdd matches both of the following JSON properties:
"issueDate":"20160303", // Should deserialize to a DateTime
"productId":"21720328", // Should deserialize to a long
And so, while the first is correctly recognized as a date, the second is also, eventually causing the "Invalid cast from 'DateTime' to 'Int64'." exception at a higher code level. It's a bit of an edge case since it's unexpected for a JSON string value corresponding to a long member to be parsable as both a long and a DateTime, but this is arguably a bug in Json.NET.
You have a couple workarounds:
Disable automatic date recognition by setting JsonSerializerSettings.DateParseHandling = DateParseHandling.None:
var dateFormatSettings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None,
DateFormatString = "yyyyMMdd"
};
Members explicitly typed as DateTime will still be correctly deserialized despite disabling low-level automatic recognition.
Use an IsoDateTimeConverter { DateTimeFormat = "yyyyMMdd" } rather than setting JsonSerializerSettings.DateFormatString.
var dateFormatSettings = new JsonSerializerSettings
{
Converters = { new IsoDateTimeConverter { DateTimeFormat = "yyyyMMdd" } }
};
Members explicitly typed as DateTime will be deserialized using your desired format string while leaving the pattern used for low-level automatic recognition unchanged.
Sample fiddle reproducing the problem and demonstrating the two workarounds.
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.