This question already has answers here:
C# JSON Serialization of Dictionary into {key:value, ...} instead of {key:key, value:value, ...}
(6 answers)
Closed 2 years ago.
I am trying to serialize the following data structure to JSON:
public class TestClass {
public TestClass() {
Translations = new List<Dictionary<string, Dictionary<string, string>>>();
}
[JsonProperty("translations")]
public List<Dictionary<string, Dictionary<string, string>>> Translations { get; set; }
}
The result json string should look like this:
„translations“: [
„xy“: {
„de-DE“: „Kommando1“,
„en-US“: „Command1“,
(…)
},
„ab“: {
„de-DE“: „Kommando2“,
„en-US“: „Command2“,
(…)
}
]
But instead, this is the output currently:
"translations": [
[
{
"Key": "action1",
"Value": [
{
"Key": "de-DE",
"Value": "Aktion 1 durchgeführt"
}
]
}
],
[
{
"Key": "Aktion2",
"Value": [
{
"Key": "cz-CZ",
"Value": "Zahajit vymenu "
},
{
"Key": "de-DE",
"Value": "Aktion2 durchführen"
},
{
"Key": "en-US",
"Value": "Execute action2"
},
{
"Key": "fr-FR",
"Value": "EXECUTER E Actione"
}
]
}
],
[
{
"Key": "Action3",
"Value": [
{
"Key": "cz-CZ",
"Value": "Vytvorit na vycisteni"
},
{
"Key": "de-DE",
"Value": "Aktion3 generieren"
},
{
"Key": "en-US",
"Value": "Action3 creation"
},
{
"Key": "fr-FR",
"Value": "GENERER MISSION"
}
]
}
], (...)
I would like to know how to serialize the dictionaries without the "key" and "value" strings, but with the values directly (like in the example above): So "en-US": "command2" instead of "key": "en-US", "value": "command2". If this is not possible using dictionary, then I would like to know which container to use in order to achieve this format.
I am writing an answer now because I got it working without any necessary work:
I get the following json-output:
[
{
"first": {
"de-De": "test1",
"de-De1": "test2",
"de-De2": "test3"
},
"second": {
"en-US": "test1us",
"en-US1": "test2us",
"en-US2": "test3us"
}
}
]
By simply doing this:
var Translations = new List<Dictionary<string, Dictionary<string, string>>>();
var dic = new Dictionary<string, string>
{
{"de-De","test1" },
{"de-De1","test2" },
{"de-De2","test3" },
};
var dic2 = new Dictionary<string, string>
{
{"en-US","test1us" },
{"en-US1","test2us" },
{"en-US2","test3us" },
};
var maindic = new Dictionary<string, Dictionary<string, string>>();
maindic.Add("first", dic);
maindic.Add("second", dic2);
Translations.Add(maindic);
var output = JsonConvert.SerializeObject(Translations, Formatting.Indented);
EDIT:
As #Selvin mentioned, it seems like you do not need the List<...> since the listed dictionaries already contain the keys you want to use. So I would go with:
var Translations = new Dictionary<string, Dictionary<string, string>>();
var dic = new Dictionary<string, string>
{
{"action1","test1" },
{"action2","test2" },
{"action3","test3" },
};
var dic2 = new Dictionary<string, string>
{
{"action1","test1us" },
{"action2","test2us" },
{"action3","test3us" },
};
Translations.Add("de-DE", dic);
Translations.Add("en-US", dic2);
var output = JsonConvert.SerializeObject(Translations, Formatting.Indented);
which ultimately leads to:
{
"first": {
"de-De": "test1",
"de-De1": "test2",
"de-De2": "test3"
},
"second": {
"en-US": "test1us",
"en-US1": "test2us",
"en-US2": "test3us"
}
}
Related
I have 3 MongoDB collections that are related to each other:
Company
Store: a Company can have multiple Stores
Product: a Store can have multiple Products
Company
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{ "$oid": "1388445c0000000000000011" },
{ "$oid": "1388445c0000000000000012" }
]
}
Store
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
}
Product
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
}
If I use Lookup to "join" the first two collections, then the ObjectIds of the Stores are replaced with their corresponding objects from the Store collection:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.ToList();
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{ "$oid": "1388445c0000000000000021" },
{ "$oid": "1388445c0000000000000022" },
{ "$oid": "1388445c0000000000000023" }
]
},
...
]
}
But I'm struggling to "join" the Products on the nested Stores.
First I tried:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "products", "_id", "products")
.ToList();
but obviously, it doesn't work as simple as that. Because the field products doesn't exist on Company, nothing happens.
If I try:
db.GetCollection<BsonDocument>("Company")
.Aggregate()
.Lookup("Store", "stores", "_id", "stores")
.Lookup("Product", "stores.products", "_id", "stores.products")
.ToList();
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": {
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
}
then the products are "joined", but all other fields of the Store are gone. Furthermore the field stores is not an array anymore, but an object.
How do I correctly setup the aggregate pipeline with the MongoDB C# Driver to get the 3 collections "joined" so that I receive the following result:
{
"_id": { "$oid": "1388445c0000000000000001" },
"name": "Company A",
"stores": [
{
"_id": { "$oid": "1388445c0000000000000011" },
"name": "Store A",
"products": [
{
"_id": { "$oid": "1388445c0000000000000021" },
"name": "Product A"
},
...
]
}
]
}
Side note:
I'm working with BsonDocument and not a concrete C# type.
I think you should achieve with nested $lookup pipeline as below:
db.Company.aggregate([
{
"$lookup": {
"from": "Store",
"let": {
stores: "$stores"
},
"pipeline": [
{
$match: {
$expr: {
$in: [
"$_id",
"$$stores"
]
}
}
},
{
$lookup: {
"from": "Product",
let: {
products: { products: { $ifNull: [ "$products", [] ] } }
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$products"
]
}
}
}
],
as: "products"
}
}
],
"as": "stores"
}
}
])
Sample Mongo Playground
And convert the query to BsonDocument with MongoDB Compass.
var pipeline = new[]
{
new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "Store" },
{ "let",
new BsonDocument("stores", "$stores")
},
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("$expr",
new BsonDocument("$in",
new BsonArray
{
"$_id",
"$$stores"
}
)
)
),
new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "Product" },
{ "let",
new BsonDocument("products",
new BsonDocument("$ifNull",
new BsonArray
{
"$products",
new BsonArray()
}
)
)
},
{ "pipeline",
new BsonArray
{
new BsonDocument("$match",
new BsonDocument("$expr",
new BsonDocument("$in",
new BsonArray
{
"$_id",
"$$products"
}
)
)
)
}
},
{ "as", "products" }
}
)
}
},
{ "as", "stores" }
}
)
};
var result = _db.GetCollection<BsonDocument>("Company")
.Aggregate<BsonDocument>(pipeline)
.ToList();
Result
Thanx to #Yong Shun I have found the correct answer.
You can build the query also with MongoDB C# types as follows:
PipelineStageDefinition<BsonDocument, BsonDocument> stage = PipelineStageDefinitionBuilder.Lookup<BsonDocument, BsonDocument, BsonDocument, IEnumerable<BsonDocument>, BsonDocument>(
db.GetCollection<BsonDocument>("Store"),
new BsonDocument("stores", new BsonDocument("$ifNull", new BsonArray { "$stores", new BsonArray() })),
new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(new List<PipelineStageDefinition<BsonDocument, BsonDocument>>
{
PipelineStageDefinitionBuilder.Match(new BsonDocumentFilterDefinition<BsonDocument>(new BsonDocument("$expr", new BsonDocument("$in", new BsonArray { "$_id", "$$stores" })))),
PipelineStageDefinitionBuilder.Lookup<BsonDocument, BsonDocument, BsonDocument, IEnumerable<BsonDocument>, BsonDocument>(
db.GetCollection<BsonDocument>("Product"),
new BsonDocument("products", new BsonDocument("$ifNull", new BsonArray { "$products", new BsonArray() })),
new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(new List<PipelineStageDefinition<BsonDocument, BsonDocument>>
{
PipelineStageDefinitionBuilder.Match(new BsonDocumentFilterDefinition<BsonDocument>(new BsonDocument("$expr", new BsonDocument("$in", new BsonArray { "$_id", "$$products" })))),
}),
"products"
)
}),
"stores"
);
List<BsonDocument> result = db.GetCollection<BsonDocument>("Entity").Aggregate().AppendStage(stage).ToList();
I have a JArray of JArrays, but I would like to flatten it into a single JArray of JObjects. I have already implemented a foreach loop which iterates through each JArray in my JArray. I need to know how to flatten each sub-JArray into a JObject.
Here is an example:
[
{
"item": [
{
"fieldName": "Name",
"value": "Andy"
},
{
"fieldName": "Phone",
"value": "678-905-9872"
}
]
},
{
"item": [
{
"fieldName": "Name",
"value": "John"
},
{
"fieldName": "Phone",
"value": "688-954-5678"
}
]
},
{
"item": [
{
"fieldName": "Name",
"value": "Ashley"
},
{
"fieldName": "Phone",
"value": "+44 671 542 8945"
}
]
},
{
"item": [
{
"fieldName": "Name",
"value": "Avi"
},
{
"fieldName": "Phone",
"value": "(212)-908-7772"
}
]
}
]
I would like each item to be a single JObject, resulting in the following JArray:
[
{
"Name": "Andy"
"Phone": "678-905-9872"
},
{
"Name": "John"
"Phone": "688-954-5678"
{
"Name": "Ashley"
"Phone": "+44 671 542 8945"
},
{
"Name": "Avi"
"Phone": "(212)-908-7772"
}
]
Thanks!
EDIT
Here is my solution (c#, using Newtonsoft.Json)
public string ParserFunction(string json)
{
string fieldname, fieldvalue;
JArray jsonArray = JArray.Parse(json);
foreach (JObject item in jsonArray)
{
JArray temp = (JArray)item["columns"]; //create new temporary JArray
foreach (JObject jobject in temp)
{
fieldname = jobject["fieldName"].ToString();
fieldvalue = jobject["value"].ToString();
item.Add(fieldname, fieldvalue);
jobject.Remove("fieldName");
jobject.Remove("value");
}
item.Remove("item");
}
json = jsonArray.ToString();
return json;
}
Not sure if this is the most optimal way to do it, I saw an answer below which looks alright as well.
var jArr = new JArray(JArray.Parse(JSON)
.Select(x => new JObject(new JProperty("Name", x["item"][0]["Name"]),
new JProperty("Phone", x["item"][1]["Phone"])
)
)
);
var str = JsonConvert.SerializeObject(jArr, Formatting.Indented);
str would be:
[
{
"Name": "Andy",
"Phone": "(785) 241-6200"
},
{
"Name": "Arthur Song",
"Phone": "(212) 842-5500"
},
{
"Name": "Ashley James",
"Phone": "+44 191 4956203"
},
{
"Name": "Avi Green",
"Phone": "(212) 842-5500"
}
]
Hello I have a dictionary that I am using.
Dictionary<string, string> myProperties
I am trying to format this into a JSON and am having difficulties. I need it to be like this:
{
"properties": [
{
"property": "firstname",
"value": "John"
},
{
"property": "lastname",
"value": "Doe"
},
{
"property": "country",
"value": "united states"
}
]
}
Currently I am using Json.NET to serialize the dictionary but that gives me this:
{
"country": "united states",
"firstname": "John",
"lastname": "Doe"
}
Can anybody help me format this into what I need. Any help would be much appreciated.
You get there by sending the result from .Select() to the json serializer wrapped in a anon class, but I would suggest that you use real classes if you intend to build something larger.
JsonConvert.SerializeObject(
new {properties = myProperties.Select(kv => new { property = kv.Key, value = kv.Value})}
,Formatting.Indented);
This will give you
{
"properties": [
{
"property": "firstname",
"value": "John"
},
{
"property": "lastname",
"value": "Doe"
},
{
"property": "country",
"value": "united states"
}
]
}
Building on N0b1ts answer:
JsonConvert.SerializeObject(new { properties = myProperties.Select(kvp => new { property = kvp.Key, value = kvp.Value }).ToList() }, Formatting.Indented);
I have a WebAPI method that returns Json in a flexible structure that depends on the request.
Part of the problem is that there could be any number of columns, and they could be any type. The 2 given below (Code and Count) are just one example.
This structure is based on the underlying classes but there could be any number of columns in the output. So, rather than the usual properties you might expect, these are objects in a collection with Name and Value properties.
The downside of this flexible approach is that it gives a non-standard format.
Is there a way to transform this into a more normalised shape? Are there maybe some attributes I can add to the class properties to change the way they are serialised?
For example, where there are 2 columns - Code (string) and Count (numeric):
Current Json:
{
"Rows": [
{
"Columns": [
{
"Value": "1",
"Name": "Code"
},
{
"Value": 13,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "2",
"Name": "Code"
},
{
"Value": 12,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "9",
"Name": "Code"
},
{
"Value": 1,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "5",
"Name": "Code"
},
{
"Value": 2,
"Name": "Count"
}
]
}
]
}
Ideally I'd like to transform it to this:
{
"Rows": [
{
"Code": "1",
"Count": 13
},
{
"Code": "2",
"Count": 12
},
{
"Code": "9",
"Count": 1
},
{
"Code": "5",
"Count": 2
}
]
}
The controller method (C#)
public ReportResponse Get(ReportRequest request)
{
var result = ReportLogic.GetReport(request);
return result;
}
The output classes
public class ReportResponse
{
public List<ReportRow> Rows { get; set; }
public ReportResponse()
{
Rows = new List<ReportRow>();
}
}
public class ReportRow
{
public List<ReportColumn> Columns { get; set; }
public ReportRow()
{
Columns = new List<ReportColumn>();
}
}
public class ReportColumn<T> : ReportColumn
{
public T Value { get; set; }
public ReportColumn(string name)
{
Name = name;
}
}
public abstract class ReportColumn
{
public string Name { get; internal set; }
}
I think the easiest way would be to map your class to a dictionary before serializing. Something like:
var dictionaries = List<Dictionary<string, object>();
foreach(var column in rows.Columns)
{
dictionaries.Add(new Dictionary<string, object>{{column.Name, column.Value}});
}
Then serialize the dictionaries variable should do the trick.
If you're using the output in JavaScript, you could translate as follows:
var
data = {
"Rows": [
{
"Columns": [
{
"Value": "1",
"Name": "Code"
},
{
"Value": 13,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "2",
"Name": "Code"
},
{
"Value": 12,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "9",
"Name": "Code"
},
{
"Value": 1,
"Name": "Count"
}
]
},
{
"Columns": [
{
"Value": "5",
"Name": "Code"
},
{
"Value": 2,
"Name": "Count"
}
]
}
]
},
output = [
];
data.Rows.forEach(function (row)
{
var
newRow = {};
row.Columns.forEach(function (column)
{
newRow[column.Name] = column.Value;
});
output.push(newRow);
})
console.log(JSON.stringify(output));
Lets see, the json can be dynamic and can probably have number of nested arrays within any property.
Example:
{
"items": [
{
"id": "0001",
"name": "Cake",
"batters": {
"batter": [
{
"id": "1001",
"type": "Regular"
},
{
"id": "1002",
"type": "Chocolate"
},
{
"dry": [
{
"id": "1003",
"type": "Devil's Food"
}
]
}
],
"other": [
{
"id": "1004",
"type": "Home Food"
}
]
},
"topping": [
{
"id": "5002",
"type": "Glazed"
},
{
"id": "5005",
"type": "Sugar"
}
]
},
{
"id": "0002",
"name": "Sweets"
}
]
}
A simple list should return elements as:
[
{
"id": "1001",
"type": "Regular"
},
{
"id": "1002",
"type": "Chocolate"
},
{
"id": "1003",
"type": "Devil's Food"
},
{
"id": "1004",
"type": "Home Food"
},
{
"id": "5002",
"type": "Glazed"
},
{
"id": "5005",
"type": "Sugar"
},
{
"id": "0002",
"name": "Sweets"
}
]
Please note:
Json can by anything, no property can be used for extraction , just knowing that what needed is stuff inside an JArray.
What i have tried so far but its just a start:
public static bool ParseJsonArray(JToken token, List<string> extracts, string parentLocation = "")
{
if (token.HasValues)
{
foreach (JToken child in token.Children())
{
if (token.Type == JTokenType.Array)
{
parentLocation += ((JProperty)token).Name;
extracts.Add(token.ToString());
}
ParseJsonArray(child, extracts, parentLocation);
}
return true;
}
else
{
return false;
}
}
token here is the parsed dynamic json.
It appears as though you want to recursively find all JArray entries that do not themselves contain nested arrays. Let's call these "leaf" array entries. I say that because you don't include the following non-leaf entry in your results:
{
"id": "0001",
"name": "Cake"
}
That being said, you can find leaf array entries with the following extension method:
public static class JsonExtensions
{
public static IEnumerable<JToken> LeafArrayEntries(this JContainer container)
{
var nonLeafEntries = new HashSet<JToken>(container.DescendantsAndSelf()
.OfType<JArray>()
.SelectMany(a => a.Ancestors().Where(p => p.Type != JTokenType.Property)));
return container.DescendantsAndSelf().Where(c => c.Parent is JArray && !nonLeafEntries.Contains(c));
}
}
Then put the returned items in an array of their own with:
var leafItemArray = new JArray(rootJContainer.LeafArrayEntries());