How to change timezone with Offset when Deserializing DateTime using JSON.NET in Azure APIM? - datetime

I'm using Azure APIM policy expression to aggregate multiple responses. In that one of the JSON response contains multiple dateTime fields. all of them are in UTC TZ. I want to deserialize all of the date fields to specific Timezone(in my case SGT +08:00) from +00:00
Input:
{
"Header": {
"UserID": "xxxxxx",
"MessageID": "xxxxxx",
"CorrelationID": "xxxx",
"DateTime": "2018-02-12T15:31:18+00:00",
"ReqID": "xxx"
},
"ResultSet": {
"Tier": {
"CardSuffix": "91",
"RetentionDeadline": "2022-02-27T16:00:00+00:00",
"PointsRequireToQualify": "xxxxx",
"QualifyingDeadline": "2022-02-27T16:00:00+00:00",
"SignupDate": "2020-08-07",
"IssuedDate": "2021-06-15",
"JoiningDateTime": "2010-03-31T10:10:00+00:00",
"RequireToUpgradeInYear": "288886",
"YearlyUpgradeDeadline": "2022-02-27T16:00:00+00:00",
"CurrentCardDesc": "xxxxxxxx",
"NextCardDesc": "xxxxxxxxx",
"CurrentTierPoints": "0",
"UpdatedDateTime": "2023-01-09T17:43:54.844+00:00",
"LastRefreshedDateTime": "2022-02-10T07:45:13+00:00"
}
}
}
Expected Output:
{
"Header": {
"UserID": "xxxxxx",
"MessageID": "xxxxxx",
"CorrelationID": "xxxx",
"DateTime": "2018-02-12T15:31:18+08:00",
"ReqID": "xxx"
},
"ResultSet": {
"Tier": {
"CardSuffix": "91",
"RetentionDeadline": "2022-02-28T0:00:00+08:00",
"PointsRequireToQualify": "xxxxx",
"QualifyingDeadline": "2022-02-28T0:00:00+08:00",
"SignupDate": "2020-08-07",
"IssuedDate": "2021-06-15",
"JoiningDateTime": "2010-03-31T10:10:00+08:00",
"RequireToUpgradeInYear": "288886",
"YearlyUpgradeDeadline": "2022-02-28T0:00:00+08:00",
"CurrentCardDesc": "xxxxxxxx",
"NextCardDesc": "xxxxxxxxx",
"CurrentTierPoints": "0",
"UpdatedDateTime": "2023-01-10T22:08:08+08:00",
"LastRefreshedDateTime": "2022-02-10T15:45:13+08:00"
}
}
}
I have tried and achieved the expected output by using ConvertTimeBySystemTimeZoneId(DateTimeOffset, String) Method, But real Problem is I don't want to add each individual Property name to change the time zone with Offset. I need generic efficient solution that deserialize DateTime fields to Specific TimeZone with the format .ToString("yyyy-MM-ddTH:mm::sszzz")
Here's my fiddle: sample
If you see my above sample, I have parse the input as JObject and converts to required format for the first property alone
obj["ResultSet"]["Tier"]["RetentionDeadline"] which changed from "RetentionDeadline": "2022-02-27T16:00:00+00:00", to "RetentionDeadline": "2022-02-28T0:00:00+08:00",
Problem with this solution:
If any new DateField is added in the backend response, I need to revisit my policy expression again and do this manual conversion. So I want generic one time conversion for all dateTime fields.
Please note that I'm writing this inside policy expression; so I can't reuse the functionality by using any c# extensions or helper methods.

You can do this by looping over the children of the JSON object:
var obj = context.Request.Body.AsJObject(true, new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None } );
foreach (JProperty x in (JToken)obj["ResultSet"]["Tier"])
{}
DateParseHandling is used for the date format does not change.
A very simple Regex is used to recognize dates 2022-02-27T:
^[0-9]{4}-[0-9]{2}-[0-9]{2}T
Complete policy:
<policies>
<inbound>
<base />
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>#{
var obj = context.Request.Body.AsJObject(true, new JsonSerializerSettings() { DateParseHandling = DateParseHandling.None } );
var pattern = #"^[0-9]{4}-[0-9]{2}-[0-9]{2}T";
var regex = new Regex(pattern);
foreach (JProperty x in (JToken)obj["ResultSet"]["Tier"])
{
string name = x.Name;
JToken tokenValue = x.Value;
if(regex.IsMatch(tokenValue.ToString()))
{
obj["ResultSet"]["Tier"][name] = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(new DateTimeOffset(Convert.ToDateTime(obj["ResultSet"]["Tier"][name])), "Singapore Standard Time").ToString("yyyy-MM-ddTH:mm::sszzz");
}
}
return obj.ToString();
}</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Result:

Update: My backend has been sending proper TimeZone data.
If your response has a Proper DateTime field with TimeZone and you want to keep the original value in TimeZone, this DateParseHandling = DateParseHandling.None can be used.
Below is an example Snippet.
https://dotnetfiddle.net/AlPJmk
This could be useful to someone.

Related

How to filter google calendar REST API events by attendee's email

I'm accessing Google Calendar REST API for Calendar Events, trying to figure out a proper notation for q parameter, to filter all events where one of the attendees is identified by email (let's say foo#bar.com)
I've tried: q=attendee.email:foo#bar.com, q=attendee.email=foo#bar.com, q=attendees.email=foo#bar.com, q=attendees.email="foo#bar.com"...
but with no results (empty list, once the q parameter is filled in)
Is it supported at all?
Is there a list of valid q parameter fields to filter by?
You cannot use any Calendar API call to directly search for attendees.
However, you can achieve this by code. You have to list all the events, loop through them and filter the events if the email you wrote coincides with the email in the attendees. For example:
function searchEvents() {
var calendarId = "primary";
var email = "test#email.com";
var result = Calendar.Events.list(calendarId).items;
for (var i = 0; i < result.length; i++){
if (result[i].attendees != undefined){ //Filters out the events without attendees
for (var j = 0; j < result[i].attendees.length; j++){
if (result[i].attendees[j].email == email){
Logger.log(result[i]); //It returns all the event information
}
}
}
}
}
The full resource object returned:
{
"kind": "calendar#calendarListEntry",
"etag": etag,
"id": string,
"summary": string,
"description": string,
"location": string,
"timeZone": string,
"summaryOverride": string,
"colorId": string,
"backgroundColor": string,
"foregroundColor": string,
"hidden": boolean,
"selected": boolean,
"accessRole": string,
"defaultReminders": [
{
"method": string,
"minutes": integer
}
],
"notificationSettings": {
"notifications": [
{
"type": string,
"method": string
}
]
},
"primary": boolean,
"deleted": boolean,
"conferenceProperties": {
"allowedConferenceSolutionTypes": [
string
]
}
}
REFERENCES:
Events List
List Resource
The "q" parameter is working like a text search in event list.
Free text search terms to find events that match these terms in the
following fields: summary, description, location, attendee's
displayName, attendee's email. Optional.
It possible to search events with specified email:
calendar.events.list(
{
q: 'attendee#email.test',
calendarId: 'primary',
timeMin: new Date().toISOString(),
maxResults: 10,
singleEvents: true,
orderBy: 'startTime',
}
It should return events where 'attendee#email.test' is specified

C# ASP.NET json objects to array

I am stuck on this error. This is my JSON data:
{
"date": "2016-08-26",
"time_of_day": "14:19",
"request_time": "2016-08-26T14:19:59+01:00",
"station_name": "Derby",
"station_code": "DBY",
"departures": {
"all": [
{
"mode": "train",
"service": "22152000",
"train_uid": "C65080"
},
{
"mode": "train",
"service": "22150000",
"train_uid": "C65145"
},
{
"mode": "train",
"service": "22180008",
"train_uid": "C70700"
}
]
}
}
What I am trying to do is add the service json object to an array, after this I want to increment through the service list and add each service to a separate API call. This is my code:
dynamic content = JsonConvert.DeserializeObject(json);
dynamic departures = content.departures;
dynamic DepartTimes = departures.all;
JArray items = ((JArray)DepartTimes["service"]);
int serviceLength = items.Count;
for (int i = 0; i < serviceLength; i++)
{
//next api call containing array increment
}
The error seems to be here :
JArray items = ((JArray)DepartTimes["service"]);
Any help is much appreciated, thank you!
One possible way is -
var json = "json";
dynamic d = JsonConvert.DeserializeObject(json);
JArray arr = new JArray();
foreach(JObject o in d.departures.all){
arr.Add(o["service"]);
}
Console.Write(arr.Count); //output : 3
Provided Json data is not proper. Second and third items must have open curly braces ({)
In addition, an example working code may be:
dynamic content = JsonConvert.DeserializeObject(json));
JArray items = (JArray)content.departures.all;
var newArray = items.Select(x=>x["service"].ToString()).ToArray();

Jquery Datatables Ajax Response

I am trying to follow the datatable example for Ajax data source (objects) found here. I am using asp.net and have the following handler which receives my data, processes it and provides the response.
public class UsersHandler : IHttpHandler
{
private const string JsHeader = #"{{""data"" {0}}}";
public void ProcessRequest(HttpContext context)
{
IEnumerable<SystemUser> data = SystemUserLogic.LoadAllSystemUsers();
List<SimpleUser> userlist = new List<SimpleUser>();
foreach (SystemUser su in data)
{
SimpleUser simple = new SimpleUser();
simple.Id = su.Id;
simple.FullName = su.NameFirst;
simple.Email = "example#email.co.uk";
userlist.Add(simple);
}
string json = JsonConvert.SerializeObject(userlist, Formatting.Indented);
context.Response.ContentType = "text/plain";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.Cache.SetNoStore();
context.Response.Expires = -1;
context.Response.Write(String.Format(JsHeader, json));
}
which deliveries the correct response when I catch it in the browser and look at the data via the network traffic. My aspx page contains the following.
$('#table_id').DataTable({
"ajax": '/Handlers_New/UsersHandler.ashx',
"columns": [
{ "data": "Id" },
{ "data": "FullName" },
{ "data": "Email" },
{ "data": "KeyResource" }
]
});
However when the page loads, I am getting this error:
DataTables warning: table id=table_id - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1
The outputted data looks like this,
{"data" [
{
"Id": 1,
"FullName": "Admin",
"Email": "example#email.co.uk",
"KeyResource": false
},
{
"Id": 2,
"FullName": "Jon",
"Email": "example#email.co.uk",
"KeyResource": false
},
{
"Id": 3,
"FullName": "Stephen",
"Email": "example#email.co.uk",
"KeyResource": false
}, etc.....
Please tell me why I am getting this error. Should I be manipulating the json object differently, or am I missing something with the Jquery datatables?
I have managed to fix my issue amazingly due to jsonlint. I ran my code through that and it turns out I was missing a ':' in my jsHeader. So what I had was:
private const string JsHeader = #"{{""data"" {0}}}";
and what I have now which now works is:
private const string JsHeader = #"{{""data"": {0}}}";
Hope this helps any one else encountering a similar issue.

How to read value when using model.save() or fetch()?

Man = Backbone.Model.extend({
url:'/test.aspx',
initialize: function(){
},
defaults: {
name:'Jim',
age: '38'
},
validate:function(attributes){
if(attributes.name == '') {
return "name can't be null";
}
}
});
var man = new Man;
man.set({name:'the5fire'});
man.save(); //the format is json{"name":"the5fire","age":38}
In test.aspx.cs, how can I read this value {"name":"the5fire","age":38} ?
I have tried Request.Form.ToString() but found no data there.
Chrome developer tools shows this json-format data in the "request payload" block.
update:
If I use man.fetch({ data: { Pagesize: '1', PageIndex: '111' } ), then on the server side, I can use Request.Params["PageIndex"]. But how can I get the name? the age?
You can access the raw body of the request via HttpRequest.InputStream and convert it to a string :
string jsonstring = new System.IO.StreamReader(Request.InputStream).ReadToEnd();
You would then deserialize this string to get an object, for example:
using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d = jss.Deserialize<dynamic>(jsonstring);
string name = (string)d["name"];

Can URI templates be used to match URIs to routes?

Frameworks like ASP.NET or Nancy provide a syntax that can be used for specifying routes, such as:
MapRoute("/customers/{id}/invoices/{invoiceId}", ...)
In ASP.NET routes work in two directions. They can match a request URI such as /customers/32/invoices/19 to a route, and they can resolve parameters such as { id: 37, invoiceId: 19 } into a URI.
RFC 6570: URI Templates also defines a similar, though much richer, specification for URI's that are often used to resolve URI's. For example:
UriTemplate("/customers/{id}/invoices{/invoiceId}{?sort}", { id: 37, invoiceId: 19, sort: 'asc' } )
// returns: /customers/37/invoices/19?sort=asc
My question is, can the syntax specified in RFC 6570 be used to match request URI's to routes? Is there a part of the syntax that would make it ambiguous to match a given URI to a given URI template? Are there any libraries that support matching a URI to a URI template?
I suspect it would be very difficult. Certainly things like the prefix syntax would make it impossible to regenerate the original parameters.
For things like path segment expansion
{/list*} /red/green/blue
How would you know which parts of the path were literals and which parts were part of the parameter? There are lots of fairly freaky behavior in the URITemplate spec, I suspect even if it is possible to match, it would be fairly expensive.
Are you interested in doing this for the purposes of routing?
It is simple regarding match but regarding resolve you need to replace the ASP.net part by the RFC 6570.
Unfortunately I am doing this in node with express js and this might not be helpful, but I am sure something like https://github.com/geraintluff/uri-templates (for resolving) is also available in ASP.
Here is some .js code to illustrate the rewriting of a hyperschema
USING RFC 6570 to use with express js (the advantage of the use within schema is that you could also define regexes for your uri templates):
var deref = require('json-schema-deref');
var tv4 = require('tv4');
var url = require('url');
var rql = require('rql/parser');
var hyperschema = {
"$schema": "http://json-schema.org/draft-04/hyper-schema",
"links": [
{
"href": "{/id}{/ooo*}{#q}",
"method": "GET",
"rel": "self",
"schema": {
"type": "object",
"properties": {
"params": {
"type": "object",
"properties": {
"id": {"$ref": "#/definitions/id"}
},
"additionalProperties": false
}
},
"additionalProperties": true
}
}
],
"definitions": {
"id": {
"type": "string",
"pattern": "[a-z]{0,3}"
}
}
}
// DOJO lang AND _
function getDottedProperty(object, parts, create) {
var key;
var i = 0;
while (object && (key = parts[i++])) {
if (typeof object !== 'object') {
return undefined;
}
object = key in object ? object[key] : (create ? object[key] = {} : undefined);
}
return object;
}
function getProperty(object, propertyName, create) {
return getDottedProperty(object, propertyName.split('.'), create);
}
function _rEscape(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function getPattern(k, ldo, customCat) {
// ...* = explode = array
// ...: = maxLength
var key = ((k.slice(-1) === '*') ? k.slice(0,-1) : k).split(':')[0];
var cat = (customCat) ? customCat : 'params'; // becomes default of customCat in TS
var pattern = '';
if (typeof ldo === 'object' && ldo.hasOwnProperty('schema')) {
var res = getProperty(ldo.schema, ['properties',cat,'properties',key,'pattern'].join('.'));
if (res) {
console.log(['properties',cat,'properties',key,'pattern'].join('.'),res);
return ['(',res,')'].join('');
}
}
return pattern;
}
function ldoToRouter(ldo) {
var expression = ldo.href.replace(/(\{\+)/g, '{') // encoding
.replace(/(\{\?.*\})/g, '') // query
.replace(/\{[#]([^}]*)\}/g, function(_, arg) {
// crosshatch
//console.log(arg);
return ['(?:[/]*)?#:',arg,getPattern(arg,ldo,'anchor')].join('');
})
.replace(/\{([./])?([^}]*)\}/g, function(_, op, arg) {
// path seperator
//console.log(op, '::', arg, '::', ldo.schema);
return [op,':',arg,getPattern(arg,ldo)].join('');
});
return {method: ldo.method.toLowerCase(), args:[expression]};
}
deref(hyperschema, function(err, fullSchema) {
console.log('deref hyperschema:',JSON.stringify(fullSchema));
var router = fullSchema.links.map(ldoToRouter);
console.log('router:',JSON.stringify(router));
});

Resources