Avoid the root element while serializing Dictionary with DataContractJsonSerializer - c#

I have a Dictionary like this which I want to serialize in Json:
xrmParentPluginContextProperties = new Dictionary<string, string> {
{ "source", className },
{ "correlationId", executionContext.ParentContext.CorrelationId.ToString() },
{ "depth", executionContext.ParentContext.Depth.ToString() },
{ "initiatingUserId", executionContext.ParentContext.InitiatingUserId.ToString() },
{ "isInTransaction", executionContext.ParentContext.IsInTransaction.ToString() },
{ "isolationMode", executionContext.ParentContext.IsolationMode.ToString() },
{ "message", executionContext.ParentContext.MessageName },
{ "mode", getModeName(executionContext.ParentContext.Mode) },
{ "operationId", executionContext.ParentContext.OperationId.ToString() },
{ "orgId", executionContext.ParentContext.OrganizationId.ToString() },
{ "orgName", executionContext.ParentContext.OrganizationName },
{ "requestId", executionContext.ParentContext.RequestId.ToString() },
{ "userId", executionContext.ParentContext.UserId.ToString() },
{ "entityId", executionContext.ParentContext.PrimaryEntityId.ToString() },
{ "entityName", executionContext.ParentContext.PrimaryEntityName },
{ "type", "Plugin" },
{ "stage", getStageName(executionContext.Stage) }
};
Then I have a helper class holding the dictionary for serialization:
public class DictionarySerializationHelperClass
{
public Dictionary<string, string> dictionary;
public DictionarySerializationHelperClass()
{
}
public DictionarySerializationHelperClass(Dictionary<string, string> dict)
{
this.dictionary = dict;
}
}
I call the serialization like this:
DictionarySerializationHelperClass dictionaryClass = new DictionarySerializationHelperClass(XrmProperties.currentPluginContext);
return SerializationHelper.JsonSerialize(dictionaryClass);
And the serialization itself:
public static string JsonSerialize<T>(T objectToSerialize) where T : class, new()
{
string serialisedJson = null;
DataContractJsonSerializer serializer;
try
{
using (MemoryStream serializationStream = new MemoryStream())
{
if (typeof(T) == typeof(Dictionary<string, string>))
{
serializer = new DataContractJsonSerializer(typeof(T), new DataContractJsonSerializerSettings()
{
UseSimpleDictionaryFormat = true,
RootName = string.Empty
});
}
else
{
serializer = new DataContractJsonSerializer(typeof(T));
}
serializer.WriteObject(serializationStream, objectToSerialize);
serializationStream.Position = 0;
var reader = new StreamReader(serializationStream);
serialisedJson = reader.ReadToEnd();
}
}
catch (Exception ex)
{
string error = $"Error on Serializing JSON: \r\n\r\n{objectToSerialize}";
throw new JsonException(error, ex);
}
return serialisedJson;
}
The problem with the result is that I always get the following json which includes the root element "dictionary":
{
"dictionary":{
"source":"asdasdasd",
"correlationId":"asdasdasd",
"depth":"1",
"initiatingUserId":"asdasdasd",
"isInTransaction":"False",
"isolationMode":"2",
"message":"Retrieve",
"mode":"Synchronus",
"operationId":"asdasd",
"orgId":"esdfsdf",
"orgName":"asdasdasd",
"requestId":"asdasdasd",
"userId":"asdasdasd",
"entityId":"asdasdasd",
"entityName":"incident",
"type":"Plugin",
"stage":"Pre-operation"
}
}
How can I remove this root element to have finally the json looking like this?
{
"source":"asdasdasd",
"correlationId":"asdasdasd",
"depth":"1",
"initiatingUserId":"asdasdasd",
"isInTransaction":"False",
"isolationMode":"2",
"message":"Retrieve",
"mode":"Synchronus",
"operationId":"asdasd",
"orgId":"esdfsdf",
"orgName":"asdasdasd",
"requestId":"asdasdasd",
"userId":"asdasdasd",
"entityId":"asdasdasd",
"entityName":"incident",
"type":"Plugin",
"stage":"Pre-operation"
}
I've tried to set
Root = string.Empty
in the DataContractJsonSerializerSettings but it seems not to help.
Any hint is highly appreciated.

Related

Merge Sparse Data into Dictionary using Json.NET PopulateObject

I would like to load sparse data in JSON format to get a result with missing data filled in with defaults, but my defaults include predefined instances of an extensible set rather than just fixed fields.
For (arbitrary) example,
Types
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
Usage
var targets = new Targets();
string json = #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}";
Newtonsoft.Json.JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Outputs Fixed=192.168.0.1:12345 Common=0.0.0.0:12345 rather than the desired Fixed=192.168.0.1:12345 Common=192.168.0.2:12345.
This shows that the desired merge logic works for fixed properties, but not for items in a Dictionary despite the fact that the Dictionary will otherwise serialize/deserialize just like a type with fixed properties.
Took me a while to figure this out. Json.NET has a dedicated function for merging two JObjects together. Here's your example modified to use this method:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" },
["Common2"] = new Link() { Addr = "192.168.0.25" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
if (link.Key != "Variable")
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var targets = new Targets();
JObject o1 = JObject.Parse( #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}");
JObject o2 = JObject.FromObject(targets);
o2.Merge(o1, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
string json = o2.ToString();
Console.WriteLine(json);
JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Console.ReadKey();
}
}
}
The output is:
{
"Fixed": {
"Addr": "192.168.0.1",
"Port": 12345
},
"Variable": {
"Common": {
"Addr": "192.168.0.2",
"Port": 12345
},
"Common2": {
"Addr": "192.168.0.25",
"Port": 80
}
}
}
Fixed=192.168.0.1:12345 Common=192.168.0.2:12345 Common2=192.168.0.25:80
EDIT by OP: Refined into extension methods without extra ToString/deserialization:
static class SerializerExtensions
{
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, T target)
{
JObject o1 = JObject.FromObject(target, serializer);
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
serializer.Populate(o1.CreateReader(), target);
return target;
}
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, JObject template)
{
JObject o1 = template.DeepClone() as JObject;
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
return serializer.Deserialize<T>(o1.CreateReader());
}
}

Convert dot notation to JSON

How do i convert dot notation to json
The dot notation can have any depth
All data is current like this:
Dictionary<string, string> data = new Dictionary<string, string>
{
{"Company.Website", "Hjemmeside"},
{"Company.TextHeaderPlaceholder", "Firmanavn"},
{"Company.User.Manager.Repositories.CreateAsync.ArgumentNullException.InvalidCompanyId", "firma id fejl"},
{"BookingSettings.HelpText", "Hjælpe tekst på webshop"},
{"BookingSettings.OnGoingOrderValidation.Text", "Bestillings validering i gang"},
{"BookingSettings.OnGoingOrderValidation.Created", "Oprettet"},
{"BookingSettings.Url", "Kundelink til booking"}
};
Json result should be:
{
"Company": {
"Website": "Hjemmeside",
"TextHeaderPlaceholder": "Firmanavn",
"Users": {
"Managers": {
"Repositories": {
"CreateAsync": {
"ArgumentNullException": {
"InvalidCompanyId": "Can not create company user with out a company!"
}
}
}
}
}
},
"BookingSettings": {
"HelpText": "Hjælpe tekst på webshop",
"OnGoingOrderValidation": {
"Text": "Bestillings validering i gang",
"Created": "Oprettet"
},
"URL": "Kundelink til booking"
}
}
How do this the easy way?
I'd start by deserializing it to nested dictionaries.
public static Dictionary<string, object> DotNotationToDictionary(Dictionary<string, string> dotNotation)
{
Dictionary<string, object> root = new Dictionary<string, object>();
foreach (var dotObject in dotNotation)
{
var hierarcy = dotObject.Key.Split('.');
Dictionary<string, object> current = root;
for (int i = 0; i < hierarcy.Length; i++)
{
var key = hierarcy[i];
if (i == hierarcy.Length - 1) // Last key
{
current.Add(key, dotObject.Value);
}
else
{
if (!current.ContainsKey(key))
current.Add(key, new Dictionary<string, object>());
current = (Dictionary<string, object>) current[key];
}
}
}
return root;
}
Once that's done you can use JsonSerializer.Serialize to convert the dictionaries to JSON (JavaScriptEncoder.UnsafeRelaxedJsonEscaping needed for "æ"):
Dictionary<string, string> dotNotation = new Dictionary<string, string>
{
{"Company.Website", "Hjemmeside"},
{"Company.TextHeaderPlaceholder", "Firmanavn"},
{"Company.User.Manager.Repositories.CreateAsync.ArgumentNullException.InvalidCompanyId", "firma id fejl"},
{"BookingSettings.HelpText", "Hjælpe tekst på webshop"},
{"BookingSettings.OnGoingOrderValidation.Text", "Bestillings validering i gang"},
{"BookingSettings.OnGoingOrderValidation.Created", "Oprettet"},
{"BookingSettings.Url", "Kundelink til booking"}
};
var betterDictionary = DotNotationToDictionary(dotNotation);
var json = JsonSerializer.Serialize(betterDictionary, new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });
Console.WriteLine(json);
Here's a fiddle with it.
This is the output:
{
"Company": {
"Website": "Hjemmeside",
"TextHeaderPlaceholder": "Firmanavn",
"User": {
"Manager": {
"Repositories": {
"CreateAsync": {
"ArgumentNullException": {
"InvalidCompanyId": "firma id fejl"
}
}
}
}
}
},
"BookingSettings": {
"HelpText": "Hjælpe tekst på webshop",
"OnGoingOrderValidation": {
"Text": "Bestillings validering i gang",
"Created": "Oprettet"
},
"Url": "Kundelink til booking"
}
}

Method inside an array in C#

I just got in contact with C# and I was wondering if it's possible to call a method inside an array. I have to say that I'm working with NoSQL database (mongodb).
This is mi code, and I want to call data() method inside that JSON.
static void Main(string[] args)
{
MongoClient client = new MongoClient();
var db = client.GetDatabase("test");
var collection = db.GetCollection<BsonDocument>("collection");
var document = new BsonDocument
{
{ "date", 10/04/2018 },
{ "data", data() }
};
collection.InsertOneAsync(document);
Console.Read();
}
static void data()
{
for (int i = 1; i <= 50; i++)
{
var data = new BsonDocument
{
{ "magnitude"+i, new BsonDocument{
{ "value", 5 }
} }
};
}
}
EDIT: Basically, what I'm trying to create with C# is this json below. I already did it with PHP and now, I'm trying to do it with C#.
{
"_id" : ObjectId("5abb735eb57dce214009035a"),
"date" : 1262300400,
"data" : {
"magnitude1" : {
"value" : 60
},
"magnitude2" : {
"value" : 38
},
"magnitude3" : {
"value" : 200
},
"magnitude4" : {
"value" : 62
},
"magnitude5" : {
"value" : 153
},
"magnitude6" : {
"value" : 176
},
"magnitude7" : {
"value" : 185
},
"magnitude8" : {
"value" : 168
},
.
.
.
You can use methods to gather data but I'm not sure exactly how you're asking it. Related to the code example I'll just give a simple run down, which is basic programming in general, not just C#.
You can write methods that return void or that return a variable of some type (at a minimum).
//Returns void
public void DoSomething()
{
//Do some work
return;
}
//Returns int
public int GetSomething()
{
int result = 100;
return result;
}
When you have methods that return data you can use them as you would a variable; just remember the method will execute every time it's called so it's often best to save the data to a variable. But for your example you can do something like this.
//other code ommitted
var document = new BsonDocument
{
{ "date", 10/04/2018 },
{ "data", getDocuments() }
};
//remaining code omitted
static List<BsonDocument> getDocuments()
{
var documents = new List<BsonDocument>();
for (int i = 1; i <= 50; i++)
{
var document = new BsonDocument
{
{ "magnitude" + i, new BsonDocument { { "value", 5 } } }
};
documents.Add(document);
}
return documents;
}
Now I modified the data() method to return a list of documents and changed the naming to match it but I'm not sure what you wanted to do with the method. That was my best assumption of what you were trying to accomplish by looking at your code so feel free to ignore all of it if it's wrong.
I've could solve it thanks to #Michael .Code below in case helps to anyone.
static void Main(string[] args)
{
MongoClient client = new MongoClient();
var db = client.GetDatabase("test");
var collection = db.GetCollection<BsonDocument>("Collection");
var document = new BsonDocument
{
{ "date", 10/04/2018 },
{ "data", new BsonDocument{ getDocuments() } }
};
collection.InsertOneAsync(document);
Console.Read();
}
static BsonDocument getDocuments()
{
var documents = new BsonDocument();
for (int i = 1; i <= 5; i++)
{
var document = new BsonDocument
{
{ "magnitude" + i, new BsonDocument { { "value", 5 } } }
};
documents.AddRange(document);
}
return documents;
}

json properties to lower case c#

I'm getting from client json string:
{ "Client": { "Name": "John" } }
but for the further handling I need the following json:
{ "client": { "name": "John" } }
I tried something like that, but it didn't help:
public class LowerCaseNamingStrategy : NamingStrategy
{
protected override string ResolvePropertyName(string name)
{
return name.ToLower();
}
}
and
var settings = new JsonSerializerSettings();
settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new LowerCaseNamingStrategy() };
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
JSON is dynamic object, so I don't know properties are there.
How can I do that with c#? With using Newtonsoft.Json or may be with using Xml.
If I understood you correctly, you need to modify properties in your Json string, but not convert the Json into object.
In this case you can try to parse Json into JObject and replace properties in that object.
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if(property.Value.Type == JTokenType.Object)// replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
property.Replace(new JProperty(property.Name.ToLower(),property.Value));// properties are read-only, so we have to replace them
}
}
sample:
var jsonString = #"{ ""Client"": { ""Name"": ""John"" } }";
var jobj = JObject.Parse(jsonString, new JsonLoadSettings());
ChangePropertiesToLowerCase(jobj);
var stringWithLowerCaseProperties = jobj.ToString(Formatting.None);
Try LowercaseContractResolver instead
var settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseContractResolver();
var json = JsonConvert.DeserializeObject(input.DataJson, settings);
Extending Anton Semenov answer for cases when JSON can contain an Array of Objects:
private static void ChangePropertiesToLowerCase(JObject jsonObject)
{
foreach (var property in jsonObject.Properties().ToList())
{
if (property.Value.Type == JTokenType.Object) // replace property names in child object
ChangePropertiesToLowerCase((JObject)property.Value);
if (property.Value.Type == JTokenType.Array)
{
var arr = JArray.Parse(property.Value.ToString());
foreach (var pr in arr)
{
ChangePropertiesToLowerCase((JObject)pr);
}
property.Value = arr;
}
property.Replace(new JProperty(property.Name.ToLower(CultureInfo.InvariantCulture), property.Value)); // properties are read-only, so we have to replace them
}
}
All other solutions here modify the original object. Here's an immutable version which returns a new object with lowercase properties:
public static class JsonExtensions
{
public static JToken ToLowerRecursive(this JToken token)
{
if (token is JObject jobj)
return new JObject(jobj.Properties().Select(x => new JProperty(x.Name.ToLowerInvariant(), x.Value.ToLowerRecursive())));
if (token is JArray jarr)
return new JArray(jarr.Select(x => x.ToLowerRecursive()));
return token;
}
}
This is easy way without Regex. Replace every [{ "A] with [{ "a]
var json = "{ \"Client\": { \"Name\": \"John\" } }";
var newJson = string.Empty;
foreach (var w in json.Split(new[] { "{ \"" }, StringSplitOptions.RemoveEmptyEntries))
{
if (w[0] != null)
{
newJson += "{ \"" + (w[0].ToString().ToLower()) + w.Remove(0,1);
}
}
result:
"{ \"client\": { \"name\": \"John\" } }"

MongoDB c# create role?

I'm trying to create a role with the c# driver.
Can anyone please tell me how to do this?
I tried several things like this:
var command = new CommandDocument(
new BsonDocument
{
{ "createRole", "Testentity_read" },
{ "privileges", new BsonArray(new BsonDocument
{
{
"resource", new BsonDocument
{
{"db", "MyDb"},
{"collection", "Testentity"}
}
},
{
"actions", new BsonArray {"read"}
}
})},
{ "roles", new BsonArray()}
}
);
var result = _database.RunCommand(command);
but always getting this exception:
".NET type MongoDB.Bson.BsonElement cannot be mapped to a BsonValue."
I found out how to create a role from C#-driver.
var command = new CommandDocument
{
{
"createRole", "Testentity_find"
},
{
"privileges", new BsonArray
{
new BsonDocument
{
{
"resource", new BsonDocument
{
{"db", "MyDb"},
{"collection", "Testentity"}
}
},
{
"actions", new BsonArray {"find"}
}
}
}
},
{ "roles", new BsonArray()}
};

Categories

Resources