JSON Schema conditional result - c#

I am trying to create a JSON schema validator. My Json schema validates a certain property value and based on that it assigns value to another property. In my C# code I want to perform an action based on that. If you notice in my schema, if country is either "United States of America" or "Canada" then, I am setting effect be "compliant" else "non-complaint". And, In my C# code, I need the value of "effect" so that I can do some further processing. Is that possible? If not, what should be my approach? I am new to Json Schema (I have seen Azure polices doing something similar to this.)
Here is my Schema
{
"type": "object",
"properties": {
"street_address": {
"type": "string"
},
"country": {
"enum": [ "United States of America", "Canada" ]
},
"effect": {"type": "string"}
},
"if": {
"properties": { "country": { "const": "United States of America" } }
},
"then": {
"effect": "Compliant"
},
"else": {
"effect": "Non-Compliant"
}
}
Here is my Document
{
"properties": {
"street_address": "1600 Pennsylvania Avenue NW",
"country": "Canada",
"postal_code": "20500"
}
}
Here is my C# Code
JObject data = null;
var currentDirectory = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName;
using (StreamReader r = new StreamReader(currentDirectory + #"/Documents/document.json"))
using (JsonTextReader reader = new JsonTextReader(r))
{
data = (JObject)JToken.ReadFrom(reader);
}
JsonSchema schema = JsonSchema.Parse(File.ReadAllText(currentDirectory + #"/Schemas/Schema.json"));
IList<string> messages;
var properties = (JObject)data["properties"];
bool valid = properties.IsValid(schema, out messages);

Related

Azure ARM deployment access a resource from a different subscription when deploying a new resource

I am using an Azure service principal and C# Azure SDK to deploy this ARM template. The publicIPPrefixResourceId I am passing as a parameter is the ARM Id of the resource that is in a different subscription from where I am currently deploying to. However, the service principal has access to both subscriptions. But I get an error saying it was not able to find the publicIPPrefixResourceId in the current subscription.
I am wondering is there a way to specify that also includes other subscriptions while looking for a resource? I don't want to use a custom-managed service identity because my service principle has access to both subscriptions.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"publicIpName": {
"type": "string"
},
"publicIpSku": {
"type": "string",
"defaultValue": "Standard"
},
"publicIPPrefixResourceId": {
"type": "string",
"metadata": {
"description": "Resource Id of the PublicIpPrefix to create VM VIP"
}
}
},
"resources": [
{
"apiVersion": "2019-02-01",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[parameters('publicIpName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('publicIpSku')]"
},
"properties": {
"publicIPAllocationMethod": "Static",
"publicIPPrefix": {
"Id": "[parameters('publicIPPrefixResourceId')]"
}
}
}
]
}
C# Azure SDK that is deploying above ARM template:
var deploymentTask = azure.Deployments.Define(parameters.DeploymentName)
.WithExistingResourceGroup(resourceGroupName)
.WithTemplate(vmTemplate)
.WithParameters(deploymentParameters)
.WithMode(DeploymentMode.Incremental)
.Create();
I don't think this is doable, while looking for resource group, you are looking under a specific subscription.
The SDK you are using is our old Azure SDK for .net, I'll suggest that you try our new SDK Azure.ResourceManager and Azure.ResourceManager.Resource, the new SDK integrates with the latest Azure.Identity, you can directly use azure resource identifier to look for resources
var client = new ArmClient(new DefaultAzureCredential());
var resourceGroup = client.GetResourceGroupResource(new Azure.Core.ResourceIdentifier("/subscriptions/<Subscriptionid>/resourceGroups/<resourcegroupname>"));
ArmDeploymentCollection ArmDeploymentCollection = resourceGroup.GetArmDeployments();
string deploymentName = "myDeployment";
var input = new ArmDeploymentContent(new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
{
TemplateLink = new ArmDeploymentTemplateLink()
{
Uri = new Uri("https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.json")
},
Parameters = BinaryData.FromObjectAsJson(new JsonObject()
{
{"storageAccountType", new JsonObject()
{
{"value", "Standard_GRS" }
}
}
})
});
ArmOperation<ArmDeploymentResource> lro = await ArmDeploymentCollection.CreateOrUpdateAsync(WaitUntil.Completed, deploymentName, input);
ArmDeploymentResource deployment = lro.Value;
Or hierarchy look for the resources like below
var client = new ArmClient(new DefaultAzureCredential());
var subscriptions =client.GetSubscriptions();
foreach(var sub in subscriptions)
{
Console.WriteLine("SubID:"+sub.Data.DisplayName);
var rgs = sub.GetResourceGroups();
foreach(var rg in rgs)
{
Console.WriteLine("RgName:"+rg.Data.Name);
ArmDeploymentCollection ArmDeploymentCollection = rg.GetArmDeployments();
string deploymentName = "myDeployment";
var input = new ArmDeploymentContent(new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
{
TemplateLink = new ArmDeploymentTemplateLink()
{
Uri = new Uri("https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.storage/storage-account-create/azuredeploy.json")
},
Parameters = BinaryData.FromObjectAsJson(new JsonObject()
{
{"storageAccountType", new JsonObject()
{
{"value", "Standard_GRS" }
}
}
})
});
ArmOperation<ArmDeploymentResource> lro = await ArmDeploymentCollection.CreateOrUpdateAsync(WaitUntil.Completed, deploymentName, input);
ArmDeploymentResource deployment = lro.Value;
}
}
Console.ReadLine();
The new version SDK is going to cover more Azure services very soon
It is possible to reference a resource the way you are trying to do. There are caveats however:
You need to provide the resourceId correctly, for example: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp" - see also resourceId function
You cannot reference a public IP prefix in another subscription. This is not a limitation of ARM but rather of the specific service.
all public IP addresses created from the prefix must exist in the same Azure region and subscription as the prefix.
Full "working" example:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"publicIpName": {
"type": "string"
},
"publicIpSku": {
"type": "string",
"defaultValue": "Standard"
},
"publicIPPrefixResourceId": {
"type": "string",
"metadata": {
"description": "Resource Id of the PublicIpPrefix to create VM VIP"
},
"defaultValue": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp"
}
},
"resources": [
],
"outputs": {
"ipPrefixInOtherSubscription": {
"type": "string",
"value": "[reference(parameters('publicIPPrefixResourceId'), '2021-08-01').ipPrefix]"
}
}
}
If you try to use it like you intended, you get this error:
{
"code": "ResourceReferenceUsesWrongSubscriptionId",
"message": "Property publicIPPrefix of resource /subscriptions/11111111-0000-0000-0000-000000000000/resourceGroups/rg-temp/providers/Microsoft.Network/publicIPAddresses/test can only reference resources in subscription(s) 11111111-0000-0000-0000-000000000000. Value /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-tmp/providers/Microsoft.Network/publicIPPrefixes/prefixtemp references wrong subscription.",
"details": []
}
And there is no way around that.

Properly format a message for slack in bot framework

I'd like to send a message with buttons to the slack channel. I'm using the bot framework (c#). I want to use the "blocks" (attachments are deprecated according to the slack api docs). So I composed a sample message in the slack "Bot Kit Builder":
The json for it looks like this:
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red",
"emoji": true
},
"value": "red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue",
"emoji": true
},
"value": "blue"
}
]
}
]
As I understand, I have to provide this content in the ChannelData property of the message I sent to the channel:
if (turnContext.Activity.ChannelId == Channels.Slack)
{
message = turnContext.Activity.CreateReply();
message.ChannelData = ChannelDataBuilder.Create("Which pill do you want to take?", "Red", "Blue");
}
The code of the ChannelDataBuilder looks like this:
public static dynamic Create(string text, params string[] choices)
{
var blocks = new List<Block> { new Section { Text = new Text { TextValue = text } } };
var elements = choices.Select(
c => new Button { Text = new Text { TextValue = c, Type = "plain_text" }, Value = c });
blocks.Add(new Actions { Elements = elements.ToArray() });
return JArray.FromObject(blocks, new JsonSerializer { NullValueHandling = NullValueHandling.Ignore });
}
The resulting json of this method looks like this:
{[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]}
So, basically I wonder how I should generate this array of json object using c#. Currently the array is still surrounded by the curly brackets (the list object), but I guess I have to provide an array of json objects.
I've already tried using the JsonConvert class and setting the ChannelData as string. But then nothing appears in the slack channel.
The channelData property allows you to pass a complete Slack message, but your are missing the required top-level properties.
If you want to include blocks, than those have to be defined under the blocks property.
So your JSON need to look more like this (not include the channelData property):
{
"blocks":
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Which pill do you want to take?"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Red"
},
"action_id": "9e8ea9fb9267484a9f02b1837f716f69",
"value": "Red"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "Blue"
},
"action_id": "34c3d9509fc04e2ea37ed54a70b78486",
"value": "Blue"
}
]
}
]
}
See here for the relevant documentation for the botframework.
And here you can see how a message payload for Slack is defined.
Update
As mentioned by #mdrichardson the botframework currently does not support blocks. (See this issue on their github)
So while syntactically correct this solutions does currently not work.
Until botframework supports blocks I would suggest to use secondary attachments.

C# Get Json sub values

I have a API response has a structure like shown below. How can I get the values from this Json string?
[
{
"id": xxx,
"profileId": xxx,
"recipientId": xxx,
"creationTime": "xxxx",
"modificationTime": "xxxx",
"active": true,
"eligible": true,
"balances": [
{
"balanceType": "AVAILABLE",
"currency": "EUR",
"amount": {
"value": 55555,
"currency": "EUR"
},
"reservedAmount": {
"value": 0,
"currency": "EUR"
},
"bankDetails": {
"id": xxx,
"currency": "EUR",
"bankCode": "code",
"accountNumber": "account number",
"swift": "swift",
"iban": "iban",
"bankName": "bankName",
"accountHolderName": "accountHolderName",
"bankAddress": {
"addressFirstLine": "bankAddress",
"postCode": "xxxxx",
"city": "xxxxx",
"country": "xxxxx",
"stateCode": null
}
}
}
]
}
]
I am using The below extension method to get data for some other API's I have integrated in my system. This works fine for the other API's I have integrated.
public static string GetJsonField(RestSharp.IRestResponse value, string res)
{
Newtonsoft.Json.Linq.JObject json = Newtonsoft.Json.Linq.JObject.Parse(value.Content);
res = json.GetValue(res).ToString();
return res;
}
Thanks in advance
Copy that Json in json2csharp.com, it will give you the classes that you will need to convert the Json in a c# object.
Then just use var myJson = JsonConverter.deserialize(Json) ;
And you can access to the Json properties as you do with any other class

how to build a good c# data structure

my aim is to develop a custom search based on goeuro.com(overview) in my spare time.
I simplyfied the search parameters to the minimum.
For example(you can try this, as loong as the search_id is valid):
http://www.goeuro.com/GoEuroAPI/rest/api/v5/results?&search_id=428558909
The search_id will be generated when you visit http://www.goeuro.com and enter the first time your search parameters.
This is the simplified data structure I generated from http://json2csharp.com/ using this as my input JSON:
{
"serviceVersion": "v1.0.0",
"sortVariants": null,
"companies": {
"1007": {
"name": "Eurolines Germany",
"logoUrl": "http://cdn-goeuro.com/static_content/web/logos/{size}/eurolines_germany.png",
"id": "1007"
}
},
"outbounds": {
"3624107261930718525-38-flight-1-27": {
"companyId": "38",
"mode": "flight",
"duration": "873",
"outboundId": "3624107261930718525",
"journeyId": "27",
"departureTime": "2017-01-15T19:12:00.000+01:00",
"arrivalTime": "2017-01-16T09:45:00.000+01:00",
"stops": "1",
"price": 16209,
"updatedAt": "1",
"segments": [ 1344486303, 574447503, 689435565, 833161604 ],
"arrivalOvernightOffset": 1,
"overnightOffset": 1
}
},
"query": {
"roundTrip": false,
"airportToAirport": false,
"locationsOutsideEurope": false,
"searchId": "428558909",
"departurePosition": "377001",
"arrivalPosition": "398532",
"departureDate": "2017-01-15T00:00:00.000+01:00",
"passengers": {
"adults": 1,
"children": 0,
"infants": 0
},
"userInfo": {
"userLocale": "en",
"userCurrency": "EUR"
},
"searchModes": {
"bus": {
"status": "done",
"resultsQty": 13,
"filteredResultsQty": 13
},
"flight": {
"status": "done",
"resultsQty": 276,
"filteredResultsQty": 276
},
"train": {
"status": "done",
"resultsQty": 4,
"filteredResultsQty": 4
}
}
},
"itineraries": [
{ "outboundLegId": "3624107261930718525-38-flight-1-27" }
],
"segmentDetails": {
"1002857016": {
"type": "flight",
"departureTime": "2017-01-16T08:35:00.000+01:00",
"arrivalTime": "2017-01-16T12:05:00.000+02:00",
"departurePosition": "313870",
"arrivalPosition": "314920",
"duration": "150",
"company": "190",
"transportId": "ca6199",
"direction": "OUTBOUND",
"overnightOffset": 1,
"departureOvernightOffset": 1,
"arrivalOvernightOffset": 1
}
},
"positions": {
"2091737": {
"positionType": "busstation",
"name": "Warszawa, Warszawa Centralny",
"cityName": "Warsaw",
"latitude": 52.22782,
"longitude": 21.00224
}
},
"currencies": {
"EUR": {
"name": "Euro",
"symbol": "€"
}
}
}
The result from http://json2csharp.com/ is pretty good, but on the other side it generates something like this
public class Outbounds
{
public __invalid_type__362410726193071852538Flight127 __invalid_name__3624107261930718525-38-flight-1-27 { get; set; }
}
I see two problems here:
using __invalid_name__3624107261930718525-38-flight-1-27 is not a valid identifier in c#
and the above mentioned name is some random name, which I can not rely on my data structure.
So, my actually questions are:
How can I handle the request ?
How shall the data structure look like ?
By the way this is the code I am using(plus the generated results from http://json2csharp.com/):
static void Main()
{
var client = new RestClient("https://www.goeuro.com");
var request = new RestRequest("/GoEuroAPI/rest/api/v5/results?&search_id=428558909", Method.GET);
request.RequestFormat = DataFormat.Json;
// contentType: "application/json; charset=utf-8",
var response = client.Execute<Response>(request).Data;
}
Actually I found an existing solution(partly) for my problem:
https://github.com/evgenTraytyak/goeuro-api
, which is actually written in node.js, but I need c#, but by the way this example does not quite work(only if you have an existing search_id) and at the end I want to get the search_id from goeuro, without manually type it to my code.
Maybe the reason that using this is that the JSON-Format changed...

Deserializing Chrome Bookmark JSON Data in C#

In response to a question I asked a few days ago, I'm attempting to stretch myself a little, and do something that I've not really focussed on much before. I've done some searching (both here, and in general), but can't find the answers (or even reasonable hints) to what I want to achieve (though, a few things come close-ish).
Basically, I'm trying to deserialize the data for the Google Chrome bookmarks file using the Json.NET library (though, if there's a better alternative, I'm all for that - the documentation for this library is a little confusing in places). I'm a little confused as to the next step to take, due primarily to being used to PHP's fantastic handling of JSON data (using json_decode()), allowing for a single function call, and then simple associative-array access.
The library (Json.NET) wants me to specify an Object type that it can deserialize the JSON data into, but I'm not really sure how to go about structuring such an Object, given the format of the Bookmarks file itself. The format is something along the lines of:
{
"roots": {
"bookmark_bar": {
"children": [ {
"children": [ {
"date_added": "12880758517186875",
"name": "Example URL",
"type": "url",
"url": "http://example.com"
}, {
"date_added": "12880290253039500",
"name": "Another URL",
"type": "url",
"url": "http://example.org"
} ],
"date_added": "12880772259603750",
"date_modified": "12880772452901500",
"name": "Sample Folder",
"type": "folder"
}, {
"date_added": "12880823826333250",
"name": "Json.NET",
"type": "url",
"url": "http://james.newtonking.com/pages/json-net.aspx";
} ],
"date_added": "0",
"date_modified": "12880823831234250",
"name": "Bookmarks bar",
"type": "folder"
},
"other": {
"children": [ ],
"date_added": "0",
"date_modified": "0",
"name": "Other bookmarks",
"type": "folder"
}
},
"version": 1
}
Now, in PHP, I'd be far more used to doing something along the lines of the following, to get the data I wanted, and ending up with 'Json.NET':
$data['roots']['bookmark_bar']['children'][0]['name'];
I can work out, simply enough, what objects to create to represent the data (something like a root object, then a bookmark list object, and finally an individual bookmark object) - but I'm really not sure as to how to implement them, and then get the library to deserialize into the relevant objects correctly.
Any advice that can be offered would be greatly appreciated.
It is not necessary to declare a type that reflects the json structure:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
using System;
class Program
{
static void Main(string[] args)
{
string json =
#"
{
""roots"": {
""bookmark_bar"": {
""children"": [ {
""children"": [ {
""date_added"": ""12880758517186875"",
""name"": ""Example URL"",
""type"": ""url"",
""url"": ""http://example.com""
}, {
""date_added"": ""12880290253039500"",
""name"": ""Another URL"",
""type"": ""url"",
""url"": ""http://example.org""
} ],
""date_added"": ""12880772259603750"",
""date_modified"": ""12880772452901500"",
""name"": ""Sample Folder"",
""type"": ""folder""
}, {
""date_added"": ""12880823826333250"",
""name"": ""Json.NET"",
""type"": ""url"",
""url"": ""http://james.newtonking.com/pages/json-net.aspx""
} ],
""date_added"": ""0"",
""date_modified"": ""12880823831234250"",
""name"": ""Bookmarks bar"",
""type"": ""folder""
},
""other"": {
""children"": [ ],
""date_added"": ""0"",
""date_modified"": ""0"",
""name"": ""Other bookmarks"",
""type"": ""folder""
}
},
""version"": 1
}
";
using (StringReader reader = new StringReader(json))
using (JsonReader jsonReader = new JsonTextReader(reader))
{
JsonSerializer serializer = new JsonSerializer();
var o = (JToken)serializer.Deserialize(jsonReader);
var date_added = o["roots"]["bookmark_bar"]["children"][0]["date_added"];
Console.WriteLine(date_added);
}
}

Categories

Resources