How do you create objects when beforehand you don't know all it's properties?
I want to create a application which works with source, schema and destination
Source is a dynamic object from database
Mapping schema is generate from json or another way
Destination object, object generate bases source and mapping schema.
Let's say you receive the following source data
{
"ModuleName": "example name",
"type": "string"
}
And you receive another dynamic JSON mapping file which specifies how some properties should be mapped from the JSON:
"mappings": [
{
"source": {
"name": "ModuleName",
"type": "String"
},
"destination": {
"name": "Module",
"type": "Single Line string"
}
},
]
I want to convert to:
{
"Module": "example name",
"type": "Single Line string"
}
how can i do this using .NET?
Every help is welcome, thanks
Related
I am trying to integrate with salesforces new grpc change data capture event bus. Events are sent to clients via grpc with an avro encoded message of what the changes to the records were, so the client has to decode the message using an avro schema that is provided and unable to be changed.
I am able to easily decode the avro encoded message for objects with fields of two union types but fields with three types throw exceptions.
This is the Name field for the Account avro schema:
{
"name": "Name",
"type": [
"null",
"string",
{
"type": "record",
"name": "Switchable_PersonName",
"fields": [
{
"name": "Salutation",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "FirstName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "LastName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "MiddleName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "InformalName",
"type": [
"null",
"string"
],
"default": null
},
{
"name": "Suffix",
"type": [
"null",
"string"
],
"default": null
}
]
}
],
"doc": "Data:Switchable_PersonName",
"default": null
},
As you can see the name can either be null, a string, or an object called Switchable_PersonName.
Using the avrogen cli tool, I was able to convert the avro schema into concrete c# classes of AccountChangeEvent, ChangeEventHeader, ChangeType, Address, Switchable_PersonName.
The Name field was created in the AccountChangeEvent class as:
private object _Name;
This is the method I made for decoding the avro message:
public static void DeserializeAccountConcrete(byte[] payload)
{
var accSchema = Avro.Schema.Parse(File.ReadAllText("./avro/AccountGRPCSchema.avsc"));
var unionSchema = accSchema as Avro.UnionSchema;
var cache = new ClassCache();
cache.LoadClassCache(typeof(AccountChangeEvent), unionSchema);
cache.LoadClassCache(typeof(Switchable_PersonName), unionSchema);
cache.LoadClassCache(typeof(Address), unionSchema);
var reader = new ReflectReader<AccountChangeEvent>(accSchema, accSchema, cache);
using var accStream = new MemoryStream(payload);
accStream.Seek(0, SeekOrigin.Begin);
var accDecoder = new BinaryDecoder(accStream);
var accEvent = reader.Read(accDecoder);
Console.WriteLine(accEvent.Name);
Console.WriteLine("Event " + accEvent.ChangeEventHeader.changeType);
}
This sort of deserialization works for other schemas but it fails for the Account schema with this exception being thrown.
Avro.AvroException: Class for union record type com.sforce.eventbus.Switchable_PersonName is not registered.Create a ClassCache object and call LoadClassCache
Looking at the documentation for avro my implementation looks correct but it seems it is not.
I have changed the field type to
private com.sforce.eventbus.Switchable_PersonName _Name;
and any other code that may rely on this field but the same error is still thrown.
I am new to avro so there may be many things that I do not know or am doing wrong.
I have a complex object (seen below) that is stored on a server. A client (browser) will pull this object down and present it flattened on a webpage. The user can then edit any field data and send the field as updated text to the server. The object will updated according to only the fields that have been changed. What is the best way to do this in C#? Since the "mobile" field is nested, how would a create a mapping?
Request with changed fields (example):
{ "mobile": "999-999-9999" }
Complex Object on Server (to be updated):
{
"product": "Live JSON generator",
"version": 3.1,
"releaseDate": "2014-06-25T00:00:00.000Z",
"demo": true,
"person": {
"id": 12345,
"name": "John Doe",
"phones": {
"home": "800-123-4567",
"mobile": "877-123-1234"
},
"email": [
"jd#example.com",
"jd#example.org"
],
"dateOfBirth": "1980-01-02T00:00:00.000Z",
"registered": true,
"emergencyContacts": [
{
"name": "Jane Doe",
"phone": "888-555-1212",
"relationship": "spouse"
},
{
"name": "Justin Doe",
"phone": "877-123-1212",
"relationship": "parent"
}
]
}
}
I looked at Automapper. The issue I see with it is that I have not seen an example where an Update is done, only new creation of an object.
With this code:
var button = Value.ForStruct(new Struct{
Fields={
["type"] = Value.ForString("postback"),
["title"] = Value.ForString("Call Representative"),
["payload"] = Value.ForString("+15105551234"),
}
});
var inPayload = Value.ForStruct(new Struct{
Fields ={
["buttons"] = Value.ForList(button),
["text"] = Value.ForString("try the postback"),
["template_type"] = Value.ForString("button"),
}
});
var attachment = Value.ForStruct(new Struct{
Fields ={
["payload"] = inPayload,
["type"] = Value.ForString("template"),
}
});
var msg = Value.ForStruct(new Struct{
Fields ={
["attachment"] = attachment,
});
Payload = new Struct{
Fields ={
["facebook"] = msg
}
I was able to create the following json:
"payload": {
"facebook": {"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postback",
"template_type": "button"
},
"type": "template"
}}
Now I need to create the following other format but I dont find how to do it:
"payload": {
"message": "Yes I did it"
"platform": "kommunicate",
"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postback",
"template_type": "button"
},
"type": "template"
}
I really dont find how to eliminate the first "facebook": { element and leave only:
{
"message": "Yes I did it",
"platform": "kommunicate",
"attachment":
And include message and platform at the same level. Here is the complete json I will like to generate:
"payload": {
"platform": "kommunicate",
"message": "Yes I did it",
"attachment": {
"payload": {
"buttons": [ {
"type": "postback",
"title": "Call Representative",
"payload": "+15105551234"
}],
"text": "try the postbackggggggg",
"template_type": "button"
},
"type": "template"
}
If you want to take an object and convert it to json I would recommend taking a look at Newtonsoft Json.Net library. They have plenty of examples that might help you. There is also protobuf.net library for serializing to protobuf instead of json.
Both libraries are used in similar ways, you create a class with appropriate properties and set the values you want. You will need multiple classes for nested types as in your example. Protobuf requires you to annotate the properties with attributes, while this is optional for json.net. You then send the object to the serialization library and get a string or binary data representing your object. This kind of object is often called a Data Transfer Object (DTO), since the only purpose it has is to aid in serialization or/and transfering the data to another system.
I am having a hard time figuring out what would be the correct Swagger 2.0 spec for the case where I am expecting a parameter in the query to be a list of long (C#). This is what I tried based on seeing examples where the parameter being passed in query is simple datatypes like int or boolean. But this does not seem to work. It does not look like it is getting parsed correctly.
My URI is somethng like this :
https://.../testinstance/FeatureTexts?api-version=2016-09-13&featureIds=1629988727&featureIds=1924980024
And in my API-level test it does not gets resolved to anything similar after the part api-version=2016-09-13&featureIds=
"get": {
"tags": [
"FeatureText"
],
"operationId": "RenderFeatureTexts",
"description": "The operation to get feature texts for specified features",
"parameters": [
{
"name": "featureIds",
"in": "query",
"required": true,
"schema": {
"type": "array",
"collectionFormat": "multi",
"items": {
"type": "integer",
"format": "int64"
}
},
.......
C# code generated by Swagger Codegen:
public static async System.Threading.Tasks.Task<object> ListFeatureTextsAsync(this IAgentClient operations, object featureIds, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{
using (var _result = await operations.ListFeatureTextsWithHttpMessagesAsync(featureIds, null, cancellationToken).ConfigureAwait(false))
{
return _result.Body;
}
}
Change the parameter definition as shown below, that is, move type, items and collectionFormat out of schema. In OpenAPI 2.0, schema is only used for body parameters, and other parameter types use type etc. directly.
"parameters": [
{
"name": "featureIds",
"in": "query",
"required": true,
"type": "array",
"collectionFormat": "multi",
"items": {
"type": "integer",
"format": "int64"
}
You can easily catch syntax errors like this by pasting your spec into Swagger Editor.
On rare occasion when receiving the message "Additional text found in JSON string after finishing deserializing object." the first thing i do is fix some JSON structure typo of mine using JSONLint.
However this time the lint says the json is valid and i can't see where the problem lies. (As i don't control the data's source i need to ldeserialize to a generic object that i then traverse. - Is this possibly the error on my part?)
I'm deserializing with Unity specific Newtonsoft.Json (JsonNet-Lite)
object resultData = JsonConvert.DeserializeObject<object>(jsonString);
What am i missing?
JSON String
"{\"statements\":[{\"id\":\"14b6382c-ddb9-44c5-a22c-a133cec50711\",\"actor\":{\"objectType\":\"Agent\",\"mbox_sha1sum\":\"f7f99253cf6ede467c3a5425d05bfcd96e524595\",\"name\":\"My Name\"},\"verb\":{\"id\":\"https://w3id.org/xapi/dod-isd/verbs/completed\",\"display\":{\"en-US\":\"completed\"}},\"result\":{\"success\":true,\"completion\":true,\"duration\":\"PT0.05S\"},\"context\":{\"contextActivities\":{\"grouping\":[{\"id\":\"http://example.com/activities/MyActivity_Grandparent\",\"objectType\":\"Activity\"}],\"parent\":[{\"id\":\"http://example.com/activities/MyActivity_Parent\",\"objectType\":\"Activity\"}]}},\"timestamp\":\"2018-02-23T19:18:34.145Z\",\"stored\":\"2018-02-23T19:18:34.145Z\",\"authority\":{\"objectType\":\"Agent\",\"account\":{\"homePage\":\"http://cloud.scorm.com\",\"name\":\"abcdef-ghijk\"},\"name\":\"Test Provider\"},\"version\":\"1.0.0\",\"object\":{\"id\":\"http://example.com/activities/MyActivity\",\"definition\":{\"extensions\":{\"https://w3id.org/xapi/dod-isd/extensions/interactivity-level\":\"3\",\"https://w3id.org/xapi/dod-isd/extensions/ksa\":\"Attitude\",\"https://w3id.org/xapi/dod-isd/extensions/category\":\"Responding\",\"http://example.com/TerminalObjective\":\"My Activity Objective\"},\"name\":{\"en-US\":\"My Activity Name\"},\"description\":{\"en-US\":\"My Activity Description\"},\"type\":\"http://adlnet.gov/expapi/activities/simulation\"},\"objectType\":\"Activity\"}}],\"more\":\"/tc/Z5R2XATQZW/sandbox/statements?continueToken=e50555fe-0c3d-4663-91c4-7f0ff7df4ccf\"}"
JSON Formatted for readability
{
"statements": [{
"id": "14b6382c-ddb9-44c5-a22c-a133cec50711",
"actor": {
"objectType": "Agent",
"mbox_sha1sum": "f7f99253cf6ede467c3a5425d05bfcd96e524595",
"name": "My Name"
},
"verb": {
"id": "https://w3id.org/xapi/dod-isd/verbs/completed",
"display": {
"en-US": "completed"
}
},
"result": {
"success": true,
"completion": true,
"duration": "PT0.05S"
},
"context": {
"contextActivities": {
"grouping": [{
"id": "http://example.com/activities/MyActivity_Grandparent",
"objectType": "Activity"
}],
"parent": [{
"id": "http://example.com/activities/MyActivity_Parent",
"objectType": "Activity"
}]
}
},
"timestamp": "2018-02-23T19:18:34.145Z",
"stored": "2018-02-23T19:18:34.145Z",
"authority": {
"objectType": "Agent",
"account": {
"homePage": "http://cloud.scorm.com",
"name": "abcdef-ghijk"
},
"name": "Test Provider"
},
"version": "1.0.0",
"object": {
"id": "http://example.com/activities/MyActivity",
"definition": {
"extensions": {
"https://w3id.org/xapi/dod-isd/extensions/interactivity-level": "3",
"https://w3id.org/xapi/dod-isd/extensions/ksa": "Attitude",
"https://w3id.org/xapi/dod-isd/extensions/category": "Responding",
"http://example.com/TerminalObjective": "My Activity Objective"
},
"name": {
"en-US": "My Activity Name"
},
"description": {
"en-US": "My Activity Description"
},
"type": "http://adlnet.gov/expapi/activities/simulation"
},
"objectType": "Activity"
}
}],
"more": "/tc/Z5R2XATQZW/sandbox/statements?continueToken=e50555fe-0c3d-4663-91c4-7f0ff7df4ccf"
}
The deserialization method, you chose, is rather for a case, when you are deserializing a well-known Json to an object, which matches the structure.
In this case the Json structure does not match the System.Object type structure, you are trying to deserialize to. The library complaints, that there's much more in the Json, than in the System.Object structure.
For parsing any Json object, you could try an example from the Newtonsoft.Json documentation:
string json = #"{
CPU: 'Intel',
Drives: [
'DVD read/writer',
'500 gigabyte hard drive'
]
}";
JObject o = JObject.Parse(json);
You will still need to traverse the deserialized object. Depending on what you want to achieve, it may actually be much better to look for a .Net class that matches the Json structure (or write one).