Parse JSON and find element with closest matching date - c#

I have a JSON string containing some data.
I do not have types declared to match and am using dynamics, as there are many, many instances where API calls are made and JSON with totally different structures are returned.
What I am trying to do is loop through the array of entries in the first element of days - and get the entry where it's dateTime closely matches the current date time.
The error I am having is :
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot
implicitly convert type Newtonsoft.Json.Linq.JArray' tostring'. An
explicit conversion exists (are you missing a cast?)
I believe there's some added complexity with the need to use dynamics, and navigating through the reasonably complex data structure.
Here is what I have tried.
dynamic windData = JsonConvert.DeserializeObject(resultWindAPI); //resultWindAPI is the JSON presented below.
string windEntries = windData.forecasts.wind.days[0].entries;
dynamic windEntryData = JsonConvert.DeserializeObject(windEntries);
//find closest wind time to get most recent data
int min = int.MaxValue;
DateTime now = new DateTime();
dynamic currentWindEntry = windEntryData[0];
foreach(dynamic entry in windEntryData)
{
DateTime thisDateTime = entry.dateTime;
if (Math.Abs(thisDateTime.Ticks - now.Ticks) < min)
{
min = (int)thisDateTime.Ticks - (int)now.Ticks;
currentWindEntry = entry;
}
}
//Do something with the currentWindEntry
Here is an example of the JSON I am trying to parse.

According to the error message
string windEntries = windData.forecasts.wind.days[0].entries;
is the problem.
You already have access to the entries via the dynamic variable before so just assign it.
dynamic windEntryData = windData.forecasts.wind.days[0].entries;

Related

c#: string(json) to object(generic) conversion adds extra braces

I have a json as string. I want to convert it into an object. But during conversion, everything is fine, except i get an extra braces outside of the object. That is not a valid json.
string st = "{\"Category\":\"test\"}";
var someType = JsonConvert.DeserializeObject(st);
//output of someType is {{"Category": "test"}}
//expected output {"Category": "test"}
I tried "JObject.Parse()" too. But the result is the same. It adds extra braces to the object.
I want the output as an object compulsorily.
Is there anything that i'm doing wrong? Am i missing something?
In the context of what you're asking, JsonConver.DeserializeObject(st) is doing exactly what you're asking it to do. You're asking it to convert a string representation of the "object" {"Category": "test"} to a json object. The problem with your approach, is that the compiler does not know how to interpret that string as anything other than an object, so it wraps it in a JSON object.
To get the result you're looking for, without declaring a POCO (i.e. deserializing an anonymous type), you'd need to do something like this
var definition = new { Category = "" };
var data = #"{'Category':'Test'}";
var me = JsonConvert.DeserializeAnonymousType(data, definition);
Console.WriteLine(me);
Adding another solution, given what was asked for in the comments.
dynamic deserialized = JObject.Parse("{\"Category\":\"test\"}");

Unable to save Date to salesforce?

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
}
});
}

How can I show a string representation of a nullable DateTime type?

The user supplies "from" and "to" months for a report to be generated (e.g., from 1 month back to 13 months back; if they choose this on 2/15/2016, the return values are 1/1/2015 and 1/1/2016).
I want to allow the user to select either the furthest back or the nearest month from either the "from" or the "to" combobox. I just want to use the furthest back in time as the "from" and the closest in time as the "to" to avoid confusion on their part (they can do whatever seems natural to them).
So I start off with this code:
int fromMonths = Convert.ToInt32(comboBoxProduceUsageFrom.Text);
DateTime RptParamsNominalFromDate = ReportSchedulerConstsAndUtils.SubtractMonthsFrom(fromMonths, nextGenerateAndSendDate);
int toMonths = Convert.ToInt32(comboBoxProduceUsageTo.Text);
DateTime RptParamsNominalToDate = ReportSchedulerConstsAndUtils.SubtractMonthsFrom(toMonths, nextGenerateAndSendDate);
..and then I want to set the "from" date to the furthest back in time and the "to" to the nearer in time. I first tried this:
DateTime RptParamsFromDate;
DateTime RptParamsToDate;
if (RptParamsNominalFromDate > RptParamsNominalToDate)
{
RptParamsFromDate = RptParamsNominalToDate;
RptParamsToDate = RptParamsNominalFromDate;
}
else if (RptParamsNominalToDate > RptParamsNominalFromDate)
{
RptParamsFromDate = RptParamsNominalFromDate;
RptParamsToDate = RptParamsNominalToDate;
}
...but that fails with, "Use of unassigned local variable 'RptParamsFromDate'" (and the same error for "RptParamsToDate").
So I tried giving the DateTimes a value/nonvalue like so:
DateTime RptParamsFromDate = null;
DateTime RptParamsToDate = null;
...but that gives me, "Cannot convert null to 'System.DateTime' because it is a non-nullable value type"
So I set my fingers in motion again and tried nullablizing the DateTimes:
DateTime? RptParamsFromDate = null;
DateTime? RptParamsToDate = null;
...but then I get, "'System.Nullable' does not contain a definition for 'ToLongDateString' and no extension method 'ToLongDateString' accepting a first argument of type 'System.Nullable' could be found (are you missing a using directive or an assembly reference?)"
This is due to this code:
RptParamsFromDate.ToLongDateString()
in this block:
MessageBox.Show(string.Format(
"Using the current configuration, the Produce Usage report would next be sent on {0} and emailed to {1}; the report would cover data from {2} to {3}",
nextGenerateAndSendDate.ToLongDateString(),
emailRecipients,
RptParamsFromDate.ToLongDateString(),
RptParamsToDate.ToLongDateString()));
So what can I do to show the DateTime value and still appease the cantankerous beast?
UPDATE
Incorporating info from both SLaks and crashmstr, I ended up with the following working method:
private void buttonTestProdUsageSettings_Click(object sender, EventArgs e)
{
// Show example of when the report will run, and using which parameters,
// using the current configuration
DateTime nextGenerateAndSendDate = GetNextProduceUsageGenerateAndSendDate();
string emailRecipients = string.Join(",", emailAddresses.ToArray());
int fromMonths = Convert.ToInt32(comboBoxProduceUsageFrom.Text);
DateTime RptParamsNominalFromDate = ReportSchedulerConstsAndUtils.SubtractMonthsFrom(fromMonths, nextGenerateAndSendDate);
int toMonths = Convert.ToInt32(comboBoxProduceUsageTo.Text);
DateTime RptParamsNominalToDate = ReportSchedulerConstsAndUtils.SubtractMonthsFrom(toMonths, nextGenerateAndSendDate);
if (RptParamsNominalFromDate.Equals(RptParamsNominalToDate))
{
MessageBox.Show("The \"from\" and \"to\" values must differ; please try again.");
return;
}
// Allow user to enter either the nearest or furthest value in either the "from" or the "to":
DateTime? RptParamsFromDate = null;
DateTime? RptParamsToDate = null;
if (RptParamsNominalFromDate > RptParamsNominalToDate)
{
RptParamsFromDate = RptParamsNominalToDate;
RptParamsToDate = RptParamsNominalFromDate;
}
else if (RptParamsNominalToDate > RptParamsNominalFromDate)
{
RptParamsFromDate = RptParamsNominalFromDate;
RptParamsToDate = RptParamsNominalToDate;
}
MessageBox.Show(string.Format(
"Using the current configuration, the Produce Usage report would next be sent on {0} and emailed to {1}; the report would cover data from {2} to {3}",
nextGenerateAndSendDate.ToLongDateString(),
emailRecipients,
RptParamsFromDate.HasValue ? RptParamsFromDate.Value.ToLongDateString() : "No \"from\" Date",
RptParamsToDate.HasValue ? RptParamsToDate.Value.ToLongDateString() : "No \"to\" Date"));
}
You're trying to use the value of a nullable type.
To do that, you need to access its .Value property, which returns a regular DateTime value.
Beware that that will throw an exception if it is in fact null.
To add on to Slaks's original answer, the reason you were getting the first answer was because you must be trying to reference RptParamsFromDate or its kin, later in the code. The problem is because of this:
You've done:
Create Variable
Is something true? No? ...okay
Is something else true? No? ...okay
Variable has still not been set to anything. (Because RptParamsNominalFromDate == RptParamsNominalToDate)
(Assumption) You've tried to access the variable.
Use of unassigned local variable 'RptParamsFromDate'" (and the same error for "RptParamsToDate
Setting it to a DateTime? will get past that, somewhat, but you need to look into the logic issue, first. Make sure you're checking that this is null, before you try to use it, later.

C# 4.0 - 3 value pairs (possibly more). Sort based on values

This is a tough one to phrase as a search query and I'm having no luck. And the more I think about it, it is more a logic question than a syntax one. However I am a newby to C# (8 years php) and I am currently building my third windows forms app so there may be a method built to do just what I want.
What I am doing is reading a date format given by the user as a single string and breaking it into parts to be assigned to an Array, or from what I have seen in my searches for Associative Arrays, maybe a SortedList or Dictionary.
e.g.
SortedList<string, int> resultArray = new SortedList<string, int>();
string dateFormat = "yyyyMMdd" // Just and example
int yearPos = dateFormat.IndexOf("yyyy");
int monthPos = dateFormat.IndexOf("MM");
int dayPos = dateFormat.IndexOf("dd");
resultArray.Add("yearPos", yearPos);
resultArray.Add("monthPos", monthPos);
resultArray.Add("dayPos", dayPos);
// So, resultArray expressed as an array looks like:
// resultArray["yearPos"] = 0
// resultArray["monthPos"] = 4
// resultArray["dayPos"] = 6
// Sort List and reassign keys (or values) based on their position value (which is unique)
// ???????
return resultArray;
Ideally, the finished result that I am after for this collection/array is to have the members ranked by the value of their position in the string. Like this:
// resultArray["yearPos"] = 1
// resultArray["monthPos"] = 2
// resultArray["dayPos"] = 3
The reason I am trying to do this, is because the same date format is used to pull out a real date from a file using Regex.Match. And I want to use these new values to know which group element of the match to use for each portion of the date.
Any help getting my head around this would be greatly appreciated.
I tried this and it works:
DateTime dt;
if (DateTime.TryParseExact("20110223", "yyyyMMdd", null, 0, out dt))
Console.WriteLine(dt);
Just use DateTime.TryParse. You can pass it a formatting string and it will do all the work for you.

Accessing DataContractJsonSerializer<T> properties via re-factored foreach loop

I have the code below.
The line string content = twitterMsg.text; is creating the error 'Use of unassigned local variable' for twitterMsg. I don't seem able to access my TwitterSearchResponse.results.text fields in my DataContractJsonSerializer<TwitterMain> collection.
TwitterSearchResponse.results is an array (set of object properties) with several string fields attached with names like text and user_info.
Can anyone help with this??
Updated code below. I am still highly confused about why I am not able to iterate over my TwitterSearchResponse.results properly and assign content = twitterMsg.text
For what it's worth, here is my DataContractJsonSerializer method:
String url = String.Format("http://search.twitter.com/search.json?q={0}&rpp=20", Server.UrlEncode(txtSearchFor.Text));
// parse the JSON data
using (MemoryStream ms = new MemoryStream(wc.DownloadData(url)))
{
DataContractJsonSerializer jsonSerializer =
new DataContractJsonSerializer(typeof(TwitterMain));
TwitterSearchResponse = jsonSerializer.ReadObject(ms) as TwitterMain; // read as JSON and map as TwitterOut
}
And here is the original posted code where the issue lies.
public List<MatchCollection> returnMatches(DataContractJsonSerializer<TwitterMain> TwitterSearchResponse)
{
List<MatchCollection> messageLinks = new List<MatchCollection>();
foreach (TwitterResult twitterMsg in TwitterSearchResponse.results)
{
string content = twitterMsg.text;
// capture internet protocol pre-fixed words from message
string pattern = #"...";
messageLinks.Add(Regex.Matches(content, pattern, RegexOptions.IgnoreCase));
// capture #username twitter users from message
string atUsernamePattern = #"#([a-zA-Z0-9-_]+)";
MatchCollection PeopleMatches = Regex.Matches(content, atUsernamePattern, RegexOptions.IgnoreCase);
}
return messageLinks;
}
I suspect it's actually reporting the use of the unassigned local variable MessageLinks. Your use of twitterMsg looks fine.
So, the big question is: what do you want to return if there aren't any results? If you're happy returning null, just assign the value when you declare MessageLinks.
Next question: do you really only want to return the last MatchCollection you find? That's what the current behaviour is: you're looping over all the variables, setting the same local variable each time (i.e. replacing the previous value) and then returning that last value.
Final question: any reason why you've got a camel-cased method name (returnMatches), a Pascal-cased local variable (MessageLinks), a Pascal-cased parameter name (TwitterSearchResponse) and a camel-cased property (text)? I would assume that text is due to it coming from JSON that way - but it's a good idea to follow normal .NET naming conventions otherwise.

Categories