I have a Web API middle layer which consumes an API which exposes a field which carries a timestamp as string (the field is string and it contains a value like "2016-05-31T14:12:45.753Z").
The proxy classes in the middle tier are generated using Visual Studio from Swagger endpoint and under the hood the object is deserialized using Json.NET.
I can see that the field was received as string (that's good):
inputObject {{ "When": "2016-05-31T14:12:45.753Z" }} Newtonsoft.Json.Linq.JToken {Newtonsoft.Json.Linq.JObject}
However, even though the target field is string the value of inputObject["When"] is a parsed as a timestamp.
inputObject["When"] {31/05/2016 14:12:45} Newtonsoft.Json.Linq.JToken {Newtonsoft.Json.Linq.JValue}
Then
JToken whenValue = inputObject["When"];
if (whenValue != null && whenValue.Type != JTokenType.Null)
{
this.When = ((string)whenValue);
}
In the end this.When is a string with value 31/05/2016 14:12:45.
Is there an option to prevent json.net from parsing the date and then casting it to string again?
Please remember that this transformation happens in auto generated code so I'm looking for some way of decorating the field on the server side which would make Swagger mark it somehow and then the generated classes would avoid the deserialize/serialize issue.
Something like:
[JsonProperty("This really is a string, leave it alone")]
public string When { get; private set; }
(Answering my own question)
I needed a solution quickly and this is my temporary solution, for the record.
I format the date as
"When": "2016-05-31 14:12:45"
and not
"When": "2016-05-31T14:12:45.753Z"
This prevents it from being interpreted. The front end (javascript) code knows that timestamps from the API are UTC and it appends 'Z' before transforming the timestamp to local time and formatting for display, e.g:
<td>{{vm.prettyDateTimeFormat(item.StatusDate+'Z')}}</td>
The ctrl code:
vm.prettyDateTimeFormat = function (dateString)
{
var momentDate = moment(dateString, "YYYY-MM-DD HH:mm:ssZZ");
if (typeof(momentDate) === "undefined" || (!momentDate.isValid()))
{
return dateString;
}
//The format needs to be sortable as it ends up in the grid.
var nicePrettyDate = momentDate.format('YYYY-MM-DD HH:mm:ss');
return nicePrettyDate;
}
As far as I don't like this solution it carried us through the demo. This issue is obviously in the back log now to be addressed properly.
[JsonIgnore]
public string When { get; private set; }
Related
I am having trouble parsing a JSON from the Alpha Vantage API. The output of the JSON is as follows
{
"Realtime Currency Exchange Rate": {
"1. From_Currency Code": "BTC",
"2. From_Currency Name": "Bitcoin",
"3. To_Currency Code": "CNY",
"4. To_Currency Name": "Chinese Yuan",
"5. Exchange Rate": "43211.50782620",
"6. Last Refreshed": "2018-04-11 17:48:12",
"7. Time Zone": "UTC"
}
}
I am parsing it like this
using (var wc = new System.Net.WebClient())
json = wc.DownloadString(link);
dynamic stuff = JsonConvert.DeserializeObject(json);
string test = stuff["Realtime Currency Exchange Rate"]["5. Exchange Rate"];
However when I run the code, I am getting this error
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot perform runtime binding on a null reference'
Anyone have any idea? If I print the value in a textbox, the value shows up but afterwards it shows that error. I am coding in c#
Thanks
Try creating an object to serialize to and use the JsonProperty attribute to map the json properties to the C# properties:
public class RealtimeCurrencyExchangeRate
{
[JsonProperty("1. From_Currency Code")]
public string CurrencyCode { get; set; }
}
Then use the correct type when deserializing.
var obj = JsonConvert.DeserializeObject<RealtimeCurrencyExchangeRate >(json);
References:
Spaces handling: Deserializing JSON when fieldnames contain spaces
DeserializeObject: https://www.newtonsoft.com/json/help/html/DeserializeObject.htm
Or, if you want to dynamically read the properties, you can create a custom contract resolver:
public class AlphaVantageApiContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
// derive the C#property name from the JSON property name
var cSharpPropertyName = propertyName;
// Remove all periods from the C#property name
cSharpPropertyName = cSharpPropertyName.Replace(".", "");
// replace all spaces with underscores
cSharpPropertyName = cSharpPropertyName .Replace(" ", "_");
// The value you return should map to the exact C# property name in your class so you need to create classes to map to.
return cSharpPropertyName;
}
}
and use this while deserializing:
var jsonSettings = new JsonSerializerSettings();
jsonSettings.ContractResolver = new AlphaVantageApiContractResolver();
var obj = JsonConvert.DeserializeObject<MyClass>(json, jsonSettings);
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_DefaultContractResolver.htm#!
You will still have to create classes that map to the objects but now you don't have to worry about the JsonProperty attribute on every property. Just remember to remove periods and replace spaces with underscores in your C# property names. You will also have to write some code to remove numbers at the beginning of property names because this isn't allowed in C#.
If you can't pre-define your classes, you will need to deserialize anonymous objects and work with that.
Your issue more than likely stems from your JSON. Though your JSON is valid, you violate a couple of principals which may be affecting the binder.
You have spaces for your property name, which are often translated into underscores.
You have a period in your property name, this also may interfere with the dot notation.
You could do the approach recommended by Kyle to bind. If you would like to use dynamic, then you could fix the above, then do the following syntax.
dynamic json = JsonConvert.Deserialize<object>(...);
var value = json.realTimeCurrencyExchangeRate.fromCurrencyCode;
The C# binder doesn't like a period when you utilize dot notation and spaces, when they're translated with underscores. So you could use your space, but the translation may be different. You would need to look closer at your object.
To my surprise , its not st.forward thing to do; saving Date to Salesforce .
I'm trying to update one field which is of type Date but it throws me some weird error .
Code :
var objSer = new JavaScriptSerializer();
string json = objSer .Serialize(new{
startdate = sfdcValue
});
MyUpdateMethod("objectName/" + id, json);
I tried to convert date to IS0 8601 standard (as suggested over SO)
1.) DateTime.UtcNow.ToString("s",System.Globalization.CultureInfo.InvariantCulture)
2.) DateTime.UtcNow.ToString("o")
Error Info :
{"message":"Cannot deserialize instance of double from VALUE_STRING
value 2017-05-26T10:31:40.5790708Z or request may be missing a
required field at [line:1, column:2]","errorCode":"JSON_PARSER_ERROR"}
You didn't elaborate on which method you are using to communicate between the server and client. I am using Javascript Remoting (#RemoteAction on the apex method) and I ran into this issue. For me, the date and datetime fields were being expected by Salesforce as date serials (your mileage may vary if using a different access method).
Given I was dealing with a dynamic list of fields, I ended up with a pair of marshall / unmarshall functions on the client that created client-only slave fields I removed before sending the data back to Salesforce (NB: the example below is javascript not c#). In your case, a marshall / unmarshall may take a different approach:
// Provide an client-only date field based on a date serial (SFDC input)
function createDateDerivedField(currentRecord, fieldName) {
Object.defineProperty(currentRecord, fieldName + '__ui', {
enumerable: true,
get: function () {
return currentRecord[fieldName] == null ? null : new Date(currentRecord[fieldName]);
},
set: function(newValue) {
// Update the original field
currentRecord[fieldName] = newValue == null ? null : (new Date(newValue)).getTime(); // Convert back to date serial
}
});
}
I'm using Newtonsoft.Json to parse json objects but one object that have date and time in it wont be parsed as it's stated.
JObject a = JObject.Parse(response);
a will get the whole json and last is the object i want.
, "last_activity_date": "2017-03-29T18:05:38.707Z"}
var date = a["last_activity_date"];
will output
date = 2017-03-29 18:05:38
Is it possible to keep the date time as it is? 2017-03-29T18:05:38.707Z
Or do i need to use regex?
Due to default JObject parsing configuration, your last_activity_date will be treated as Date type, if you want to treat it as a string, you'll need to create dedicated class and deserialize your JSON into object.
public class Root
{
[JsonProperty("last_activity_date")]
public string LastActivityDate { get; set; }
}
You can use JsonConvert:
var obj = (Root)JsonConvert.DeserializeObject(json, typeof(Root));
Console.WriteLine(obj.LastActivityDate); //outputs: 2017-03-29T18:05:38.707Z
I am using the CsvHelper package to write my C# models to Csv. I am using fluent class maps (inheriting from CsvClassMap) to define my field to property mappings.
The issue I have is that some of the property values look like dates to excel. For example "2 - 4". I expect the end user to use excel to view these CSV's. I do not want these values to show as dates, so I am looking to have CsvHelper surround this field by quotes. However, I want ONLY this field surrounded by quotes. There are OTHER fields containing data I WANT to be interpreted (e.g. dates). Can I configure my mapping to specify this field should be quoted? I've played with using a type converter, but that's clearly the wrong approach because this is converting the VALUE and not instructing how to format the field.
As of version 12 you can do this:
const int indexToQuote = 4;
csv.Configuration.ShouldQuote = (field, context) =>
context.Record.Count == indexToQuote &&
context.HasHeaderBeenWritten;
So, apparently quoting is not what I needed to do. Excel quite helpfully decides to treat numeric values that look remotely like dates as dates, unless the field begins with a space (which it then will not display). I feel like relying on this is rather hackish, but I'll take it. FWIW, here's the type converter I used:
public class LeadingSpaceTypeConverter : DefaultTypeConverter {
public override string ConvertToString( TypeConverterOptions options, object value ) {
if (value == null ) {
return String.Empty;
}
return String.Concat(" ", value.ToString());
}
}
And the fluent code:
Map( m => m.CompanySize ).TypeConverter<LeadingSpaceTypeConverter>().Index( 4 );
I am coding in C# on Visual Studio 2013 and I'm trying to deserialize some JSON using ServiceStack 3.9.71. They are contained in files (not under my control) and when I try to deserialize it, I end up with the correct array of DTO's but in addition, I have a null object at the end of the array. I've narrowed it to a carriage return ("\r") at the end of the file. A few solutions I can do is to trim the string, or remove all the "\r", or disable CRLF auto switch in GIT and just be super diligent when committing, however, I feel that seems like a "hack". I feel that DeserializeFromString should be able to handle carriage returns at the end of the string. On the brighter side, when I'm run the same code on OSX, it works perfectly fine since the file is now in Unix format that only uses linefeeds and not a combination of carriage returns and line feeds.
Has anyone else seen this? Any recommended fixes besides the ones I've mentioned?
To prove it to myself, I wrote a simple test (fails both Windows and OSX).
My DTO:
class DeserializeTestData
{
public int someData { get; set; }
public String moreData { get; set; }
}
My Test:
[Test]
public void ShouldNotContainNullItemsWhenDeserializing()
{
var deserializeMe = "[\r\n\t{\r\n\t\t\"someData\": 1,\r\n\t\t\"moreData\": \"I'm data!\r\nokok\r\n\"\r\n\t\t},\r\n\t{\r\n\t\t\"someData\": 2,\r\n\t\t\"moreData\": \"I'm also some data!\"\r\n\t\t}\r\n]\r\n";
var rows = ServiceStack.Text.JsonSerializer.DeserializeFromString<DeserializeTestData[]>(deserializeMe);
foreach (var row in rows)
{
Assert.That(row, Is.Not.EqualTo(null));
}
}
The results:
Test Name: ShouldNotContainNullItemsWhenDeserializing
Test Outcome: Failed
Test Duration: 0:00:00.125
Result Message:
Expected: not null
But was: null
Manipulating the input values for \r\n etc. becomes dicey.
also, afaik, JsConfig doesn't involve a setting to ignore null values on deserialization.
the best available option is to just ignore null Rows in the post-deserialized values.
var nonNullRows = rows.Where(r => r != null).ToList();
not the most elegant, but under the conditions, should do the job.