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.
Related
My JSON string is like this:
{
"number": "1",
"optionsList": [
"{\"answer\":\"42\", \"options\":\"washington\"}",
"{\"answer\":\"65\", \"options\":\"new england\"}"
]
}
I understand the optionsList JSON is malformed and not correct JSON but I cannot change this and would like to handle this in my code. Such that it can be deserialized correctly into a c# class.
I did:
Root.cs
public class Root {
public Root() {
Number = new Number();
OptionsList = new List<OptionsList>();
}
[JsonProperty(PropertyName = "number")]
public string Number { get; internal set; }
[JsonProperty(PropertyName = "optionsList")]
public List<OptionsList> OptionsList { get; internal set; }
}
OptionsList.cs
public class OptionsList {
[JsonProperty(PropertyName = "answer")]
public string Answer { get; internal set; }
[JsonProperty(PropertyName = "options")]
public string Options { get; internal set; }
}
Deserializing like this: deserializedResponse = JsonConvert.DeserializeObject<T>(response); //response is the JSON Object
By simply doing this, I am getting an exception.
Exception trace:
---> Newtonsoft.Json.JsonSerializationException: Error converting value "{"answer":"42","options":"washington"}" to type 'Types.OptionsList'. Path 'OptionsList[0]', line 1, position 1947.
---> System.ArgumentException: Could not cast or convert from System.String to Types.OptionsList.
at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType)
at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType)
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.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
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.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonConverter[] converters)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonConverter[] converters)
How can I deserialize optionsList into a c# class correctly?
Maybe if I can deserialize each string of optionsList individually before this, it would work but I don't know where to do this. Or can I add something in the constructor of OptionsList or the Root class itself to do this?
you have an array of json strings inside of a json string. So you have to deserialize the nesteded json strings at first
var jObj = JObject.Parse(json);
jObj["optionsList"] = new JArray(jObj["optionsList"]
.Select(x => JObject.Parse((string)x)));
Root data = jObj.ToObject<Root>();
I have successfully set up, configured, and run the MassTransit (v8.0.3) JobConsumers sample and am now trying to customize to my project. After modifying, I'm getting deserialization issues when submitting my job:
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MyNamespace.Foo' 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<T> 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.
Path 'foos[0]'.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
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.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
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.Serialization.JsonSerializerProxy.DeserializeInternal(JsonReader reader, Type objectType)
at MassTransit.Serialization.JsonConverters.InterfaceProxyConverter.CachedConverter`1.MassTransit.Serialization.JsonConverters.BaseJsonConverter.IConverter.Deserialize(JsonReader reader, Type objectType, JsonSerializer serializer) in /_/src/MassTransit.Newtonsoft/Serialization/JsonConverters/InterfaceProxyConverter.cs:line 31
at MassTransit.Serialization.JsonConverters.BaseJsonConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in /_/src/MassTransit.Newtonsoft/Serialization/JsonConverters/BaseJsonConverter.cs:line 15
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.JsonSerializer.Deserialize[T](JsonReader reader)
at MassTransit.Serialization.NewtonsoftObjectDeserializer.DeserializeObject[T](Object value, T defaultValue) in /_/src/MassTransit.Newtonsoft/Serialization/NewtonsoftObjectDeserializer.cs:line 34
at MassTransit.Serialization.BaseSerializerContext.DeserializeObject[T](Object value, T defaultValue) in /_/src/MassTransit/Serialization/EnvelopeSerializerContext.cs:line 52
at MassTransit.JobServiceEventExtensions.GetJob[TJob](ConsumeContext`1 context) in /_/src/MassTransit/JobServiceEventExtensions.cs:line 24
at MassTransit.JobService.FinalizeJobConsumer`1.Consume(ConsumeContext`1 context) in /_/src/MassTransit/JobService/JobService/FinalizeJobConsumer.cs:line 45
at MassTransit.Middleware.MethodConsumerMessageFilter`2.MassTransit.IFilter<MassTransit.ConsumerConsumeContext<TConsumer,TMessage>>.Send(ConsumerConsumeContext`2 context, IPipe`1 next) in /_/src/MassTransit/Middleware/MethodConsumerMessageFilter.cs:line 27
at MassTransit.Middleware.LastPipe`1.Send(TContext context) in /_/src/MassTransit.Abstractions/Middleware/Middleware/LastPipe.cs:line 30
at MassTransit.Consumer.DelegateConsumerFactory`1.Send[TMessage](ConsumeContext`1 context, IPipe`1 next) in /_/src/MassTransit/Consumers/Consumer/DelegateConsumerFactory.cs:line 29
at MassTransit.Consumer.DelegateConsumerFactory`1.Send[TMessage](ConsumeContext`1 context, IPipe`1 next) in /_/src/MassTransit/Consumers/Consumer/DelegateConsumerFactory.cs:line 39
at MassTransit.Middleware.ConsumerMessageFilter`2.MassTransit.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext`1 context, IPipe`1 next) in /_/src/MassTransit/Middleware/ConsumerMessageFilter.cs:line 46
I am using MassTransit.Newtonsoft for JSON support because I use it in my project and due to issues encountered when upgrading to MassTransit v8. It is configured here:
x.UsingRabbitMq((context, cfg) =>
{
cfg.UseNewtonsoftJsonSerializer();
cfg.UseNewtonsoftJsonDeserializer();
...
}
Here is my message definition (that replaces the sample's ConvertVideo), stripped down to the smallest failing case:
public interface ProcessFoos
{
List<Foo> Foos { get; }
}
where Foo is defined as:
public class Foo
{
public string FooID { get; set; }
}
I'm initiating a new job from a different consumer by calling:
var foos = new List<Foo>
{
new Foo { FooID = "foo" }
};
Response<JobSubmissionAccepted> response = await _processFoosClient.GetResponse<JobSubmissionAccepted>(new
{
Foos = foos
});
My job consumer looks like this:
public class ProcessFoosJobConsumer : IJobConsumer<ProcessFoos>
{
public async Task Run(JobContext<ProcessFoos> context)
{
// Never gets here due to the deserialization issue
var processFoos = context.Job;
...
}
}
Based on the MassTransit docs and everything I've found while researching, it seems like this should be supported and I'm probably just missing something simple.
When using Entity Framework with the built-in JobServiceDbContext, the Job property in the JobSaga table is serialized to JSON using System.Text.Json – which is not compatible with the serialization of Newtonsoft when it comes to nested object (the job message type is converted to a Dictionary<string,object>).
I don't know of a current fix at this point, since there isn't a way to dynamically specify the serializer based upon the incoming message types for the DbContext.
Or you could create your own mappings, and your own DbContext, with the Newtonsoft serializer methods instead of the ones used in v8.
Your best bet when using job consumers and the job service sagas with Cosmos or EF is to use System.Text.Json.
I have a class that I use as a message type in Rebus to publish/subscribe to, but I hit a snag when I was trying out a proof-of-concept in LinqPad. Any messages that my app receives fails with a deserialization exception. I have been able to narrow down the issue to Newtonsoft.JSON package and come up with the minimal example to demonstrate the issue:
public class MyMessage
{
public string Name { get; set; } = "";
public int Number { get; set; }
}
void Main()
{
var message = new MyMessage { Name = "ABC", Number = 5 };
var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var serialized = JsonConvert.SerializeObject(message, defaultSettings);
Console.WriteLine(serialized);
try
{
var deserialized = JsonConvert.DeserializeObject(serialized, message.GetType(), defaultSettings);
Console.WriteLine(deserialized);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
This code works fine as a console app if I compile it in Rider, and outputs the following to the console:
{"$type":"ConsoleApp1.MyMessage, ConsoleApp1","Name":"ABC","Number":5}
ConsoleApp1.MyMessage
The same code fails in LinqPad 6 on the line having JsonConvert.DeserializeObject statement with the following error message:
{"$type":"UserQuery+MyMessage, LINQPadQuery","Name":"ABC","Number":5}
Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null'. Path '$type', line 1, position 44.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, 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.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at UserQuery.Main() in C:\Users\arcaa\AppData\Local\Temp\LINQPad6\_dwfnlfnz\tufqxf\LINQPadQuery:line 19
Exception message: Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null
It looks as if the serializer is attempting to deserialize the string into a different version of the type (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5) than the one provided as a parameter (UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61). The interesting bit here is the type used in the JsonConvert.DeserializeObject method is the exact same type that's passed to the serializer and there's only one class in that code.
I don't know where the other type version comes from. Does anyone know the reason for this and if possible, how to avoid it?
Edit:
I also noticed that if I close LinqPad and open it again, the code works fine. But if I then go and make a change to the code (even if I just add a single space), the exception returns.
Most likely, there will be some static dictionary in the Newtonsoft.Json library that caches type information, keyed to strings such as "MyMessage". When you edit the query re-run it, LINQPad must re-compile the query, so there will be a new version of MyMessage sitting in a new DLL. However, the static cache in Newtonsoft.Json will still point to the old one was used in the last execution.
To work around this, you don't need to re-start LINQPad; just press Shift+F5 to clear the cached process. Alternatively, add the following code to your query:
Util.NewProcess = true;
This tells LINQPad not to cache the process for subsequent use.
There's also an option in Edit | Preferences > Advanced to never recycle processes. This incurs a small performance cost; also methods such as LINQPad's Util.Cache - which caches data between executions - will not work.
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.
Im getting this error while trying to deserialize JSON string to object :
"System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> Newtonsoft.Json.JsonSerializationException: Error converting value "{"Admin":false,"Id":1,"Password":"heslo","Nick":"jozifek"}" to type 'Entities.dbo.User'. Path 'Frontman', line 1, position 105. ---> System.ArgumentException: Could not cast or convert from System.String to Entities.dbo.User.
at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType)
at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType)
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(String value, Type type)
at BussinessLogic.helpers.Serializer.Deserialize[T](String obj) in C:\Users\Ondra\documents\visual studio 2010\Projects\webServiceTestApp\BussinessLogic\helpers\Serializer.cs:line 14
at webServiceTestApp.Service1.GetSongs(String band) in C:\Users\Ondra\documents\visual studio 2010\Projects\webServiceTestApp\webServiceTestApp\Service1.asmx.cs:line 142
--- End of inner exception stack trace ---"
User.cs looks like this:
namespace Entities.dbo
{
[TableName("tbl_user")]
public class User : AbstractEntity
{
[MapField("nick")]
public string Nick { get; set; }
[MapField("password")]
public string Password { get; set; }
[MapField("admin")]
public bool Admin { get; set; }
}
}
and Band.cs looks like this:
namespace Entities.dbo
{
[TableName("tbl_band")]
public class Band : AbstractEntity
{
[MapField("name")]
public string Name { get; set; }
[MapField("frontman")]
public int FrontmanId { get; set; }
[Association(CanBeNull = false, ThisKey = "FrontmanId", OtherKey = "Id")]
public User Frontman { get; set; }
AbstractEntity:
namespace Entities.helpers
{
public abstract class AbstractEntity
{
[PrimaryKey, Identity, MapField("id")]
public int Id { get; set; }
public string Serialize()
{
return JsonConvert.SerializeObject(this);
}
}
}
and this error pops out when im sending string to deserialize as Band containing User object in Frontman property..
any idea where I went wrong?
thanks :)
its hard to say, i have use jonson once few years ago, but
in jonson you have property "Password" but in object you have MapField("password") - in lower case
Is in AbstractEntity class "Id" property?