I have a requirement where an event will emit a JSON following a defined schema = JSON-EventA schema. I have a config(config.json) that follows another JSON-EventB schema. Using data from JSON-EventA and Config from config.json, I have to generate a JSON event data which should follow the JSON-EventB schema. Is this possible to write a function GenerateEventData which takes the eventA data and config to generate EventB data ? Without coding the POCO and writing custom logic to convert from one json to another ? I want to be able to update the JSON-EventA schema, config.json without touching the function GenerateEventData, it should be able to generate data in the new JSON-EventB schema as directed by config.json
Illustrating an example that I want -
// I can get an event data and ensure it follows JSON-EventA schema
string eventAData = GetEvent(A);
/*
eventAData = {"Title": "MyTitle", "Address": {"PING": "123"}}
Follows the below schema
EventASchema = {
"type": "object",
"properties": {
"Title": {
"type": "string"
},
"Address": {
"type": "object",
"properties": {
"PIN": {
"type": "string"
}
}
}
};*/
string config = GetConfig();
// config = {"NewTitle": "Generated ${Title}", "PIN": "000${Address.PIN}", "Country": "US"}
//This is what I want to achieve.
string eventB = GenerateEventData(A, config);
/*
eventB = {"NewTitle": "Generated MyTitle", "PIN": "000123", "Country": "US"}
*/
Related
I am trying to make a message handler for received JSON strings over MQTT. I have this working for one type of message.
Here is my code:
static void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
// handle message received
Console.WriteLine("Received = " + Encoding.UTF8.GetString(e.Message) + " on topic " + e.Topic);
string source = Encoding.UTF8.GetString(e.Message);
dynamic data = JObject.Parse(source);
Console.WriteLine(data.content.value);
// this data.content.value is working for message 1 but crashes for message 2
}
How can I avoid using a lot of try catches in my message handler in case I have 2 (or more) different MQTT messages entering?
These are my 2 types of messages for now:
/// message 1:
{
"header": {
"headeritem": "exampleheader",
"type": "headertype",
"info": "headerinfo"
},
"content": {
"name": "contentname",
"value": true
}
}
/// message 2:
{
"header": {
"headeritem": "exampleheader",
"type": "headertype",
"info": "headerinfo"
},
"content": {
"item1": "exampleitem1",
"item2": "exampleitem2",
"item3": [
{
"item3type1": "exampletype1.1",
"item3type2": "exampletype2.1",
"item3type3": "exampletype3.1",
"item3type4": ["0xFFAA"]
},
{
"item3type1": "exampletype1.2",
"item3type2": "exampletype2.2",
"item3type3": "exampletype3.2",
"item3type4": ["0xFFBB"]
},
{
"item3type1": "exampletype1.3",
"item3type2": "exampletype2.3",
"item3type3": "exampletype3.3",
"item3type4": ["0xFFCC"]
}
]
}
}
When you do not know what JSON type is comming in, you can use reflection on the dynamic data type to check if the property exists.
Like explained here:
Test if a property is available on a dynamic variable
However, when the different JSON formats are limited to only a few, maybe you are better off creating hard typed classes to parse the JSON in.
Like explained here:
How to Convert JSON object to Custom C# object?
This way you do not have to worry about whether a property exists.
We are currently working on a .NET Core application and we are using MongoDB for it. I am using .NET Driver to access the data. All the data we are saving in a collection has different types of data structure.
For example, it has first document which has Name, Phone and a Payload which has embedded document in which we are saving address:
{
"Name": "TestName",
"Phone": "23846787",
"Payload": {
"Address": "TestAddress",
"City": "TestCity"
},
"Active": true
}
Then in the same collection we have another document which has Name, Phone and a Payload which is completely different from first one:
{
"Name": "TestName2",
"Phone": "54568765",
"Payload": {
"Weight": "70",
"Age": "45",
"Gender": "Female"
}
}
Now when we use .NET driver to get both of these records, we get an error because it cannot cast the embedded document into an object (as it doesnt know about the object). We need to tell it, which type of object is the embedded document. But we dont want to do it because we have several types of payload we want to save. I tried using discriminator "_t" but it didn't help.
Can someone please suggest how we can read the data when we have different elements in the document and also has embedded documents?
You can define a convention indicating that the client must ignore the elements that it is unable to map:
var conventionPack = new ConventionPack
{
new IgnoreExtraElementsConvention(true)
};
ConventionRegistry.Register("conventions", conventionPack, _ => true);
var client = new MongoClient(options)...
I'm working on a project that involves automating API calls using a Swagger Definition. I download the swagger.json file. The structure of the JSON Object I need to parse is not consistent. When parsing paths, there are a list of objects, then within that they have the methods that can be used for that specific path. I can retrieve just the path using various string methods but my question was, is there a good way to parse json if the JSON is structured in such a way that it does not have a firm key? Here is an example of what I mean:
{"/user": {
"post": {
"tags": [
"user"
],
"summary": "Create user",
"description": "This can only be done by the logged in user.",
"operationId": "createUser",
"consumes": [
"application/json"
],
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Created user object",
"required": true,
"schema": {
"$ref": "#/definitions/User"
}
}
],
"responses": {
"default": {
"description": "successful operation"
}
}
}
}
If I wanted to just parse that path and retrieve the method object how could I go about that considering sometimes the object will be "post" or sometimes it will be "get", "put", etc depending on what is allowable for the path.
JObject jsonResp = swaggerDownload();
JObject paths = (JObject)jsonResp["paths"];
foreach (var i in paths)
{
string pathToString = i.ToString();
var shaveSomethings = pathToString.Substring(1, something.Length - 2);
var pathAndJson = shaveSomethings.Split(new[] { ',' }, 2);
string correctJsonStructure = "{\"" + pathAndJson[0] + "\":" + pathAndJson[1] + "}";
JObject bd = JObject.Parse(correctJsonStructure);
//dynamic pathsTest = JsonConvert.DeserializeObject<dynamic>(correctJsonStructure);
//JObject result = JsonConvert.DeserializeObject<JObject>(correctJsonStructure);
//Console.WriteLine(bd["/user"]);
}
The swagger.json file should have full definition of each entity that endpoints return. You can follow How to create Rest API client to get a working client.
I've dealt with an API where responses didn't always match the definition. I saved all responses to a store/log first and then would try to de-serialize JSON. In case of an exception I would go back to store/log and see what was different and update my code to accommodate for the change. After few iterations there were no new changes and the ordeal was over.
Hope that helps.
I'm using Autofac JSON files to register two classes for the same interface in my project.
If I do something like this:
JSON Config file 1:
{
"components": [
{
"type": "Services.FirstProvider, Services",
"services": [
{
"type": "Services.IHotelProvider, Services"
}
],
"parameters": {
"username": "<user>",
"password": "<pwd>"
}
}
]
}
JSON Config file 2:
{
"components": [
{
"type": "Services.SecondProvider, Services",
"services": [
{
"type": "Services.IHotelProvider, Services"
}
],
"parameters": {
"key": "<key>",
}
}
]
}
And register:
config.AddJsonFile("First/FirstProviderConfig.json");
config.AddJsonFile("Second/SecondProviderConfig.json");
I can see that only the SecondProviderhas been registered. And switching registration:
config.AddJsonFile("Second/SecondProviderConfig.json");
config.AddJsonFile("First/FirstProviderConfig.json");
Only FirstProvider has been registered.
If I try to register them in the same file:
{
"components": [
{
"type": "Services.FirstProvider, Services",
"services": [
{
"type": "Services.IHotelProvider, Services"
}
],
"parameters": {
"username": "<user>",
"password": "<pwd>"
}
},
{
"type": "Services.SecondProvider, Services",
"services": [
{
"type": "Services.IHotelProvider, Services"
}
],
"parameters": {
"key": "<key>"
}
}
]
}
It works.
I need to have separated files to configure them. What I miss?
The key point here is that you're using Microsoft.Extensions.Configuration as the basis for configuration files now, which means configuration is somewhat governed by the way Microsoft.Extensions.Configuration behaves.
When you have configuration, the way Microsoft.Extensions.Configuration wants to handle it is to override settings as you layer one configuration provider on top of another.
In the simple case, say you have two configurations:
{
"my-key": "a"
}
and
{
"my-key": "b"
}
It doesn't create an array of all possible values; it'll layer the second over the first based on the key (my-key) matching and override to have the value b.
When you parse JSON configuration it flattens everything out into key/value pairs. Does the same with XML. It does this because configuration supports environment variables and INI files and all sorts of other backing stores.
In the case of the above very simple files, you get
my-key == b
Nice and flat. Looking at something more complex:
{
"top": {
"simple-item": "simple-value",
"array-item": ["first", "second"]
}
}
It flattens out like:
top:simple-item == simple-value
top:array-item:0 == first
top:array-item:1 == second
Notice how the array (an "ordinal collection") gets flattened? Each item gets auto-assigned a fake "key" that has a 0-based index.
Now think about how two config files will layer. If I have the above more complex configuration and then put this...
{
"top": {
"array-item": ["third"]
}
}
That one flattens out to
top:array-item:0 == third
See where I'm going here? You layer that override config over the first one and you get:
top:simple-item == simple-value
top:array-item:0 == third
top:array-item:1 == second
The arrays don't combine, the key/value settings override.
You see them in a JSON representation, but it's all just key/value pairs.
You have two choices to try and fix this.
Option 1: Fudge the Array (Not Recommended)
Since your first configuration is (simplified):
{
"components": [
{
"type": "Services.FirstProvider, Services",
"services": [ ...]
}
]
}
You can potentially "fudge it" a little by putting a dummy empty element in the second "override" config:
{
"components": [
{
},
{
"type": "Services.SecondProvider, Services",
"services": [ ...]
}
]
}
Last I checked, the override thing was additive-only, so empty values don't erase previously set values. By shifting the array in the second configuration by 1, it'll change the flattened version of the key/value representation and the two arrays should "merge" the way you want.
But that's pretty ugly and I wouldn't do that. I just wanted to show you one way to make it work so you'd understand why what you're doing isn't working.
Option 2: Two Separate Configuration Modules (Recommended)
Instead of trying to combine the two JSON files, just create two separate IConfiguration objects by individually loading the JSON files. Register them separately in two different ConfigurationModule registrations. It shouldn't blow up if either of the configurations is empty.
var first = new ConfigurationBuilder();
first.AddJsonFile("autofac.json", optional: true);
var firstModule = new ConfigurationModule(first.Build());
var second = new ConfigurationBuilder();
second.AddJsonFile("autofac-overrides.json", optional: true);
var secondModule = new ConfigurationModule(second.Build());
var builder = new ContainerBuilder();
builder.RegisterModule(firstModule);
builder.RegisterModule(secondModule);
If the config is empty or missing it just won't register anything. If it's there, it will. In the case where you want to override things or add to your set of handlers for nice IEnumerable<T> resolution, this should work.
Is there any way to programmatically generate a JSON schema from a C# class?
Something which we can do manually using http://www.jsonschema.net/
Another option which supports generating JSON Schema v4 is NJsonSchema:
var schema = JsonSchema.FromType<Person>();
var schemaJson = schema.ToJson();
The library can be installed via NuGet.
Update for NJsonSchema v9.4.3+:
using NJsonSchema;
var schema = await JsonSchema.FromTypeAsync<Person>();
var schemaJson = schema.ToJson();
This is supported in Json.NET via the Newtonsoft.Json.Schema NuGet package. Instructions on how to use it can be found in the official documentation, but I've also included a simple example below.
JSchemaGenerator generator = new JSchemaGenerator();
JSchema schema = generator.Generate(typeof(Person));
Console.WriteLine(schema.ToString());
//{
// "type": "object",
// "properties": {
// "Name": {
// "type": [ "string", "null" ]
// },
// "Age": { "type": "integer" }
// },
// "required": [ "Name", "Age" ]
//}
JsonSchemaGenerator js = new JsonSchemaGenerator();
var schema = js.Generate(typeof(Person));
schema.Title = typeof(Person).Name;
using (StreamWriter fileWriter = File.CreateText(filePath))
{
fileWriter.WriteLine(schema);
}
For those who land here from google searching for the reverse (generate the C# class from JSON) - I use those fine online tools:
JSON:
http://json2csharp.com/
(Source: http://jsonclassgenerator.codeplex.com/)
XML:
http://xmltocsharp.azurewebsites.net/
(Source: https://github.com/msyoung/XmlToCSharp)