How to unflatten flattened json in C# - c#

from this Answer I learned how to flatten a JSON object in c#.
from JSON String:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
To:
The following are lines of strings, not an object
menu.id:file
menu.value:File
menu.popup.menuitem[0].value:New
menu.popup.menuitem[0].onclick:CreateNewDoc()
menu.popup.menuitem[1].value:Open
menu.popup.menuitem[1].onclick:OpenDoc()
menu.popup.menuitem[2].value:Close
menu.popup.menuitem[2].onclick:CloseDoc()
Now, i want to reverse the process.
I can found implementations from this question but it is in JavaScript.
How do I unflatten (return structured JSON from lines) it in C# with json.net?

I managed to solve it out.
Below is my code combined with Sarath Rachuri's flattening code.
I did not test it in too many cases, so it could be buggy.
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace JSONHelper
{
class JSONFlattener
{
private enum JSONType{
OBJECT, ARRAY
}
public static Dictionary<string, string> Flatten(JObject jsonObject)
{
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
{
properties.Add(jToken.Path, jToken.ToString());
return properties;
});
return results;
}
public static JObject Unflatten(IDictionary<string, string> keyValues)
{
JContainer result = null;
JsonMergeSettings setting = new JsonMergeSettings();
setting.MergeArrayHandling = MergeArrayHandling.Merge;
foreach (var pathValue in keyValues)
{
if (result == null)
{
result = UnflatenSingle(pathValue);
}
else
{
result.Merge(UnflatenSingle(pathValue), setting);
}
}
return result as JObject;
}
private static JContainer UnflatenSingle(KeyValuePair<string, string> keyValue)
{
string path = keyValue.Key;
string value = keyValue.Value;
var pathSegments = SplitPath(path);
JContainer lastItem = null;
//build from leaf to root
foreach (var pathSegment in pathSegments.Reverse())
{
var type = GetJSONType(pathSegment);
switch (type)
{
case JSONType.OBJECT:
var obj = new JObject();
if (null == lastItem)
{
obj.Add(pathSegment,value);
}
else
{
obj.Add(pathSegment,lastItem);
}
lastItem = obj;
break;
case JSONType.ARRAY:
var array = new JArray();
int index = GetArrayIndex(pathSegment);
array = FillEmpty(array, index);
if (lastItem == null)
{
array[index] = value;
}
else
{
array[index] = lastItem;
}
lastItem = array;
break;
}
}
return lastItem;
}
public static IList<string> SplitPath(string path){
IList<string> result = new List<string>();
Regex reg = new Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
foreach (Match match in reg.Matches(path))
{
result.Add(match.Value);
}
return result;
}
private static JArray FillEmpty(JArray array, int index)
{
for (int i = 0; i <= index; i++)
{
array.Add(null);
}
return array;
}
private static JSONType GetJSONType(string pathSegment)
{
int x;
return int.TryParse(pathSegment, out x) ? JSONType.ARRAY : JSONType.OBJECT;
}
private static int GetArrayIndex(string pathSegment)
{
int result;
if (int.TryParse(pathSegment, out result))
{
return result;
}
throw new Exception("Unable to parse array index: " + pathSegment);
}
}
}

Purely System.Text.Json solution for unflatteing JSON. Requires .Net 6.
private static JsonNode Unflatten(Dictionary<string, JsonValue> source)
{
var regex = new System.Text.RegularExpressions.Regex(#"(?!\.)([^. ^\[\]]+)|(?!\[)(\d+)(?=\])");
JsonNode node = JsonNode.Parse("{}");
foreach (var keyValue in source)
{
var pathSegments = regex.Matches(keyValue.Key).Select(m => m.Value).ToArray();
for (int i = 0; i < pathSegments.Length; i++)
{
var currentSegmentType = GetSegmentKind(pathSegments[i]);
if (currentSegmentType == JsonValueKind.Object)
{
if (node[pathSegments[i]] == null)
{
if (pathSegments[i] == pathSegments[pathSegments.Length - 1])
{
node[pathSegments[i]] = keyValue.Value;
node = node.Root;
}
else
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[pathSegments[i]] = JsonNode.Parse("{}");
}
else
{
node[pathSegments[i]] = JsonNode.Parse("[]");
}
node = node[pathSegments[i]];
}
}
else
{
node = node[pathSegments[i]];
}
}
else
{
if (!int.TryParse(pathSegments[i], out int index))
{
throw new Exception("Cannot parse index");
}
while (node.AsArray().Count - 1 < index)
{
node.AsArray().Add(null);
}
if (i == pathSegments.Length - 1)
{
node[index] = keyValue.Value;
node = node.Root;
}
else
{
if (node[index] == null)
{
var nextSegmentType = GetSegmentKind(pathSegments[i + 1]);
if (nextSegmentType == JsonValueKind.Object)
{
node[index] = JsonNode.Parse("{}");
}
else
{
node[index] = JsonNode.Parse("[]");
}
}
node = node[index];
}
}
}
}
return node;
}
private static JsonValueKind GetSegmentKind(string pathSegment) =>
int.TryParse(pathSegment, out _) ? JsonValueKind.Array : JsonValueKind.Object;

To flatten a JSON object:
arrayJSON.stringify()

Related

System.Text.Json to Newtonsoft Json

Is there a JsonElement and JsonValueKind equivalent in newtonsoft Json? What would be the correct code to port the below code that uses System.Text.Json to newtonsoft Json? Reason for my port in due to my dll not able to find the right System.Buffers assembly version. I followed all the advices I could get but still I am unable to resolve it. So thought of using Newtonsoft Json.
public static Dictionary<string, dynamic> JsonDeserialize(string Json)
{
JsonElement elm = JsonSerializer.Deserialize<JsonElement>(Json);
Dictionary<string, dynamic> dict = ElementToDict(elm);
return dict;
}
public static dynamic ElementToDict(JsonElement obj)
{
if (obj.ValueKind == JsonValueKind.Number)
{
return StringToDecimal(obj.GetRawText());
}
else if (obj.ValueKind == JsonValueKind.String)
{
return obj.GetString();
}
else if (obj.ValueKind == JsonValueKind.True || obj.ValueKind == JsonValueKind.False)
{
return obj.GetBoolean();
}
else if (obj.ValueKind == JsonValueKind.Object)
{
var map = obj.EnumerateObject().ToList();
var newMap = new Dictionary<String, dynamic>();
for (int i = 0; i < map.Count; i++)
{
newMap.Add(map[i].Name, ElementToDict(map[i].Value));
}
return newMap;
}
else if (obj.ValueKind == JsonValueKind.Array)
{
var items = obj.EnumerateArray().ToList();
var newItems = new ArrayList();
for (int i = 0; i < items.Count; i++)
{
newItems.Add(ElementToDict(obj[i]));
}
return newItems;
}
else
{
return null;
}
}
You can try using JToken and JTokenType:
var tok = JsonConvert.DeserializeObject<JToken>("{\"test\": 1}"); // or JToken.Parse
Console.WriteLine(tok.Type); // prints "Object"
Thanks to #guru-stron I sucessfully ported to Newtonsoft.Json. Following is my code:
public static Dictionary<string, dynamic> JsonDeserialize(string Json)
{
Console.WriteLine(Json);
var elm = JsonConvert.DeserializeObject<JToken>(Json);
// Replace double with decimal in the map
Dictionary<string, dynamic> dict = ElementToDict(elm);
return dict;
}
public static dynamic ElementToDict(JToken obj)
{
if (obj.Type == JTokenType.Float || obj.Type == JTokenType.Integer)
{
return StringToDecimal(obj.ToString());
}
else if (obj.Type == JTokenType.String)
{
return obj.ToString();
}
else if (obj.Type == JTokenType.Boolean)
{
return obj.ToObject<Boolean>();
}
else if (obj.Type == JTokenType.Object)
{
var map = obj.Children().ToList();
var newMap = new Dictionary<String, dynamic>();
foreach (JProperty m in map)
{
newMap.Add(m.Name, ElementToDict(m.Value));
}
return newMap;
}
else if (obj.Type == JTokenType.Array)
{
var items = obj.AsJEnumerable().ToList();
var newItems = new ArrayList();
for (int i = 0; i < items.Count; i++)
{
newItems.Add(ElementToDict(obj[i]));
}
return newItems;
}
else
{
return null;
}
}

Display data to Debug Console

I writing app for UWP
I have this code
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
I try to display data from dictionary like this.
Debug.WriteLine("There");
Debug.WriteLine(p.products);
But it not works.
How I can display data of dictionary ?
Which type does GetProducts() return?
If it's just a Dictionary you can do the following:
foreach(var key in p.Keys)
{
Debug.WriteLine(key);
}
To read a Dictionary --
foreach(KeyValuePair<string,string> kvp in p){
Console.WriteLine(kvp.Key);
Console.WriteLine(kvp.Value);
}
foreach(string key in p.Keys){
Console.WriteLine(key);
Console.WriteLine(p[key]);//value
}
foreach(string value in p.Values){
Console.WriteLine(value);
}
But your problem is that P is a class called products:
Product p = new Product()
{
name = "test product 8",
title = "test product 8",
description = "test product 8",
price = 8.0M
};
You would access the properties of p like:
p.name;
p.title;
p.description;
p.price;
Debug.WriteLine(p.name);
Debug.WriteLine(p.title);//etc
Using the below extension class (Requires Newtonsoft JSON library) you can get a JSON string of any object either with or without readable formatting.
Using the class to get a readable JSON string;
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
var jsonString = p.ToFormattedJsonString();
Debug.WriteLine(jsonString);
Using the class to get a plain JSON string without format;
var p = await wc.GetProducts(new Dictionary<string, string>() {
{ "orderby", "id" }, { "filter[search]", "1884" }
});
var jsonString = p.ToJsonString();
Debug.WriteLine(jsonString);
You can also simplfy the above by adding your own extension method such as the below;
public static void ToDebug(this object data)
{
Debug.WriteLine(data.ToFormattedJsonString());
}
The extension class;
using System.Text;
using Newtonsoft.Json;
namespace System
{
public static class JsonExtensions
{
public static string ToFormattedJsonString(this object obj, bool indentWithTab)
{
return indentWithTab
? ToFormattedJsonString(obj, "\t")
: ToFormattedJsonString(obj);
}
public static string ToFormattedJsonString(this object obj, string indentString = " ")
{
return FormatJson(obj.ToJsonString(), indentString);
}
public static string ToJsonString(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T DeserializeJsonString<T>(this string jsonString)
{
return JsonConvert.DeserializeObject<T>(jsonString);
}
private static string FormatJson(string jsonString, string indentString)
{
var indent = 0;
var quoted = false;
var builder = new StringBuilder();
for (var i = 0; i < jsonString.Length; i++)
{
var character = jsonString[i];
switch (character)
{
case '{':
case '[':
builder.Append(character);
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(++indent, indentString);
}
break;
case '}':
case ']':
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(--indent, indentString);
}
builder.Append(character);
break;
case '"':
builder.Append(character);
bool escaped = false;
var index = i;
while (index > 0 && jsonString[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
builder.Append(character);
if (!quoted)
{
builder.AppendLine();
builder.RepeatAppend(indent, indentString);
}
break;
case ':':
builder.Append(character);
if (!quoted)
builder.Append(" ");
break;
default:
builder.Append(character);
break;
}
}
return builder.ToString();
}
public static StringBuilder RepeatAppend(this StringBuilder builder, int count, string format,
params object[] parameters)
{
if (count <= 0 || string.IsNullOrEmpty(format))
return builder;
for (int i = 0; i < count; i++)
{
builder.AppendFormat(format, parameters);
}
return builder;
}
}
}

How to recursively populate a TreeView with JSON data

I have a winforms treeview, I can read data automatically, (a node that is equal to key, and a node inside that is equal to value), but when reading object type, the values inside it are not going to be child of object node (key of object), (maybe I couldnt explain well, here is a screenshot and my methods.)
layer0 needs to be inside textures and scale needs to be inside display
My Json:
{
"parent": "builtin/generated",
"textures": {
"layer0": "mm:items/iron_dust"
},
"display": {
"scale": [ 1.7, 1.7, 1.7 ]
}
}
My method to auto detect(not all mine actually)
private void Form1_Load(object sender, EventArgs e)
{
StreamReader reader = new StreamReader(path);
string json = reader.ReadToEnd();
reader.Close();
JObject obj = JObject.Parse(json);
getAllProperties(obj);
}
void getAllProperties(JToken children)
{
TreeNode mainNode = treeView1.Nodes[0];
mainNode.Text = Path.GetFileNameWithoutExtension(path);
foreach (JToken child in children.Children())
{
var property = child as JProperty;
if (property != null)
{
if (property.Value.Type == JTokenType.String)
{
TreeNode keyNode = mainNode.Nodes.Add(property.Name);
keyNode.Nodes.Add(property.Value.ToString());
}
if (property.Value.Type == JTokenType.Array)
{
JArray array = (JArray)property.Value;
TreeNode node = mainNode.Nodes.Add(property.Name);
for (int i = 0; i < array.Count; i++)
{
node.Nodes.Add(array[i].ToString());
}
}
if (property.Value.Type == JTokenType.Object)
{
TreeNode topNode = mainNode.Nodes.Add(property.Name.ToString());
foreach (var item in property)
{
if (item.Type == JTokenType.String)
{
if (property.Value.Type == JTokenType.String)
{
TreeNode keyNode = topNode.Nodes.Add(property.Name);
keyNode.Nodes.Add(property.Value.ToString());
}
if (property.Value.Type == JTokenType.Array)
{
JArray array = (JArray)property.Value;
TreeNode node = topNode.Nodes.Add(property.Name);
for (int i = 0; i < array.Count; i++)
{
node.Nodes.Add(array[i].ToString());
}
}
}
}
}
// Console.WriteLine(property.Name + ":" + property.Value);//print all of the values
}
getAllProperties(child);
}
}
}
I tried to get parent, but it didnt have name and value properties :S.
Any help?
(Sorry for language mistakes)
The problem is that, as you recursively descend the JToken hierarchy, you also need to recursively descend the TreeNode hierarchy you are creating, adding child nodes to the parent node just created, rather than the root node, along the lines of Recursion, parsing xml file with attributes into treeview c#.
Thus if you do:
private void Form1_Load(object sender, EventArgs e)
{
using (var reader = new StreamReader(path))
using (var jsonReader = new JsonTextReader(reader))
{
var root = JToken.Load(jsonReader);
DisplayTreeView(root, Path.GetFileNameWithoutExtension(path));
}
}
private void DisplayTreeView(JToken root, string rootName)
{
treeView1.BeginUpdate();
try
{
treeView1.Nodes.Clear();
var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(rootName))];
tNode.Tag = root;
AddNode(root, tNode);
treeView1.ExpandAll();
}
finally
{
treeView1.EndUpdate();
}
}
private void AddNode(JToken token, TreeNode inTreeNode)
{
if (token == null)
return;
if (token is JValue)
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(token.ToString()))];
childNode.Tag = token;
}
else if (token is JObject)
{
var obj = (JObject)token;
foreach (var property in obj.Properties())
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(property.Name))];
childNode.Tag = property;
AddNode(property.Value, childNode);
}
}
else if (token is JArray)
{
var array = (JArray)token;
for (int i = 0; i < array.Count; i++)
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(i.ToString()))];
childNode.Tag = array[i];
AddNode(array[i], childNode);
}
}
else
{
Debug.WriteLine(string.Format("{0} not implemented", token.Type)); // JConstructor, JRaw
}
}
You will get the following tree view structure:
Here is my crack at it. The output is identical to Notepad++'s JSTool plug-in:
The code is structured as a TreeView extension:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TestDLApp.Utilities.Extensions
{
public static class ObjectToTreeView
{
private sealed class IndexContainer
{
private int _n;
public int Inc() => _n++;
}
private static void FillTreeView(TreeNode node, JToken tok, Stack<IndexContainer> s)
{
if (tok.Type == JTokenType.Object)
{
TreeNode n = node;
if(tok.Parent != null)
{
if(tok.Parent.Type == JTokenType.Property)
{
n = node.Nodes.Add($"{((JProperty)tok.Parent).Name} <{tok.Type.ToString()}>");
}
else
{
n = node.Nodes.Add($"[{s.Peek().Inc()}] <{tok.Type.ToString()}>");
}
}
s.Push(new IndexContainer());
foreach (var p in tok.Children<JProperty>())
{
FillTreeView(n, p.Value, s);
}
s.Pop();
}
else if (tok.Type == JTokenType.Array)
{
TreeNode n = node;
if(tok.Parent != null)
{
if (tok.Parent.Type == JTokenType.Property)
{
n = node.Nodes.Add($"{((JProperty)tok.Parent).Name} <{tok.Type.ToString()}>");
}
else
{
n = node.Nodes.Add($"[{s.Peek().Inc()}] <{tok.Type.ToString()}>");
}
}
s.Push(new IndexContainer());
foreach (var p in tok)
{
FillTreeView(n, p, s);
}
s.Pop();
}
else
{
var name = string.Empty;
var value = JsonConvert.SerializeObject(((JValue)tok).Value);
if (tok.Parent.Type == JTokenType.Property)
{
name = $"{((JProperty)tok.Parent).Name} : {value}";
}
else
{
name = $"[{s.Peek().Inc()}] : {value}";
}
node.Nodes.Add(name);
}
}
public static void SetObjectAsJson<T>(this TreeView tv, T obj)
{
tv.BeginUpdate();
try
{
tv.Nodes.Clear();
var s = new Stack<IndexContainer>();
s.Push(new IndexContainer());
FillTreeView(tv.Nodes.Add("ROOT"), JsonConvert.DeserializeObject<JToken>(JsonConvert.SerializeObject(obj)), s);
s.Pop();
}
finally
{
tv.EndUpdate();
}
}
}
}
You can call it as so:
treeView1.SetObjectAsJson(new MyNeatObject());

json.net: how can I deserialize 'similar but different' external json structures to a single class

I'm looking for a way, preferably using JSON.NET (using latest), to deserialize multiple external JSON formats/structures to a single class.
simplified example (the differences are greater than this but the different json's contain similar information):
external 1
{
"id": 1234,
"person": {
"name": "john",
"surname": "doe"
}
}
external 2
{
"ref": "1234",
"firstName": "JOHN",
"lastName": "DOE"
}
internal (this is not real, it's just for show)
{
"tag": "1234",
"name1": "John",
"name2": "Doe"
}
Is there some way/library which perhaps allows you to configure the mapping using a mapping.json file. Preferably one that also allows formatting of the values etc. These are only 2 examples, but we have many more.
Edit:
We can tell/hint JSON.NET what source the given JSON is coming from. Therefor we don't have to have a single schema/contract/solution that can handle all different scenarios. We would actually prefer to have a .json mapping/transform config file for every different external json structure (to keep it future proof without the need of having to rebuild everything).
Edit 2:
What I've now done is the following based on Pavel Baravik answer is to go through all properties of a 'schema/transformation' JSON. This JSON has the same structure as the final JSON of the object that we want to transform the original external JSON to. If a token is of type 'String' we'll parse that string (supports {{ }} and plain =) and use that as a path to pull values from the original external JSON. In the meantime the final JSON is being constructed, which afterwards will be deserialized to our internal object.
I think we could improve the performance of this code by 'sort-of' compiling it using an Expression tree.
static void Main(string[] args)
{
var schema = #"
{
""person"": {
""name"": ""=test.firstName"",
""fullName"": ""{{test.firstName}} {{lastName}}"",
""surName"": ""=lastName""
}
}";
var json1 = #"
{
""test"": {
""firstName"": ""John""
},
""lastName"": ""Doe"",
}";
Console.WriteLine(Transform(json1, schema).ToString());
Console.ReadLine();
}
public static JObject Transform(string json, string schema)
{
var j = JObject.Parse(json);
var s = JObject.Parse(schema);
var t = Transform(s, j);
return t;
}
public static JObject Transform(JObject schema, JObject source)
{
var target = new JObject();
foreach (var child in schema.Children())
{
var property = child as JProperty;
if (property != null)
{
var schemaToken = property.Value;
var allowClone = true;
JToken t = null;
if (schemaToken.Type == JTokenType.Object)
{
t = Transform((JObject) schemaToken, source);
}
else if (schemaToken.Type == JTokenType.String)
{
allowClone = false;
t = TransformProperty(source, (JValue)schemaToken);
}
if (t != null || allowClone)
{
target.Add(property.Name, (t ?? property).DeepClone());
}
}
}
return target;
}
private static readonly Regex MoustacheRegex = new Regex(#"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private static JToken TransformProperty(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
EDIT: (nice JTransform class)
public class JTransform
{
private InternalJTransform _internal;
public void Load(string filePath)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream))
{
Load(new JsonTextReader(reader));
}
}
public void Load(string filePath, Encoding encoding)
{
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream, encoding))
{
Load(new JsonTextReader(reader));
}
}
public void Load(JsonReader reader)
{
_internal = new InternalJTransform(reader);
}
public JObject Transform(JsonReader sourceReader)
{
return _internal.Transform(sourceReader);
}
public JObject Transform(JObject source)
{
return _internal.Transform(source);
}
public T TransformObject<T>(object obj)
{
return _internal.TransformObject<T>(obj);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
return _internal.TransformObject<T>(source, serializer);
}
#region InternalJTransform
private sealed class InternalJTransform
{
private static readonly Regex MoustacheRegex = new Regex(#"\{\{[^\}]+\}\}", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Singleline);
private JsonSerializer _serializer;
private JObject _template;
private bool _ignoreUndefined;
public InternalJTransform(JsonReader reader)
{
var json = JObject.Load(reader);
_template = json["template"] as JObject;
_serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
_serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
_serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreUndefined"]?.Value<bool>() ?? settings["ignoreNull"]?.Value<bool>() ?? false);
}
private void Load(JsonReader reader)
{
var json = JObject.Load(reader);
var template = json["template"] as JObject;
var serializer = new JsonSerializer();
var settings = json["settings"];
if (settings["camelCase"]?.Value<bool>() ?? false)
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
if (settings["ignoreNull"]?.Value<bool>() ?? false)
serializer.NullValueHandling = NullValueHandling.Ignore;
_ignoreUndefined = (settings["ignoreNull"]?.Value<bool>() ?? false);
_serializer = serializer;
_template = template;
}
public JObject Transform(JsonReader sourceReader)
{
var obj = JObject.Load(sourceReader);
return TransformInternal(_template, obj, _serializer);
}
public JObject Transform(JObject source)
{
return TransformInternal(_template, source, _serializer);
}
public T TransformObject<T>(object obj)
{
var source = JObject.FromObject(obj);
var im = TransformInternal(_template, source, _serializer);
return im.ToObject<T>(_serializer);
}
public T TransformObject<T>(JObject source, JsonSerializer serializer = null)
{
var obj = TransformInternal(_template, source, _serializer);
return obj.ToObject<T>(serializer ?? _serializer);
}
private JObject TransformInternal(JObject template, JObject source, JsonSerializer serializer)
{
var ignoreNull = serializer.NullValueHandling == NullValueHandling.Ignore;
var target = new JObject();
foreach (var property in template.Properties())
{
var token = property.Value;
if (token.Type == JTokenType.Object)
{
token = TransformInternal((JObject)token, source, serializer);
}
else if (token.Type == JTokenType.String)
{
token = TransformStringToken(source, (JValue)token);
// handle undefined, not found, values
if (token == null && _ignoreUndefined) continue;
}
// handle real null values (this does not include null values set in the template)
if (token != null && token.Type == JTokenType.Null && ignoreNull) continue;
target.Add(property.Name, token?.DeepClone());
}
return target;
}
private JToken TransformStringToken(JObject source, JValue jstring)
{
var str = (string)jstring.Value;
JToken t = null;
// if string starts with =
if (str.StartsWith("="))
{
t = GetTokenByPath(source, str.Substring(1));
}
else
{
var allFound = true;
str = MoustacheRegex.Replace(str, m =>
{
var mv = m.Value;
var mt = GetTokenByPath(source, mv.Substring(2, mv.Length - 4));
if (mt == null) allFound = false;
return mt?.ToString() ?? string.Empty;
});
if (allFound)
t = new JValue(str.Trim());
}
return t;
}
private static JToken GetTokenByPath(JObject source, string path)
{
JToken t = null;
var pathItems = path.Split('.');
var s = source;
for (var i = 0; i < pathItems.Length && s != null; ++i, s = t as JObject)
{
t = s[pathItems[i]];
}
return t;
}
}
#endregion
}
You can firstly 'flatten' your input structures with use of JsonReader and then map to a single class (adopted from JSON.NET deserialize a specific property).
void Main()
{
var json0 = #"{
""id"": 1234,
""person"": {
""name"": ""john"",
""surname"": ""doe""
}";
var json1 = #" {
""ref"": ""1234"",
""firstName"": ""JOHN"",
""lastName"": ""DOE""
}";
foreach (var j in new []{json0, json1})
{
var name = GetFirstInstance<string>(new [] {"person.name", "firstName", "name1"}, j);
var surname = GetFirstInstance<string> (new [] {"person.surname", "lastName", "name2"}, j);
new {name, surname}.Dump();
}
}
public T GetFirstInstance<T>(string[] path, string json)
{
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.PropertyName && path.Contains((string)jsonReader.Path))
{
jsonReader.Read();
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(jsonReader);
}
}
return default(T);
}
}
Take a look at the CustomCreationConverter in JSON.NET http://www.newtonsoft.com/json/help/html/CustomCreationConverter.htm you can make different converters and decide which one to use based on the JSON you have. they could all output the same class

Roslyn can't find IDictionary.Add interface implementation member

Why Roslyn in following example can't find IDictionary.Add interface member implementation in Dictionary type?
IDictionary.Add and Dictionary.Add correctly resolve by Roslyn, but then i can't find implementation IDictionary.Add method in Dictionary type.
Update
I added second code example with correct code.
VS2015, Roslyn 1.1.1:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
namespace RoslynSymbolsTest
{
public class InterfaceMemberImplemnentationTest
{
public void Run()
{
string solutionPath = #"..\..\..\RoslynSymbolsTest.sln";
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionPath).Result;
var project = solution.Projects.Where(p => p.Name == "RoslynSymbolsTest").Single();
var document = project.Documents.Where(d => d.Name == "InterfaceMemberImplemnentationTest.cs").Single();
var semanticModel = document.GetSemanticModelAsync().Result;
// IDictionary.Add
IMethodSymbol _idictionaryAddMethodSymbol = ResolveMethod(semanticModel, typeof(IDictionary<,>), "Add"); // ok
// Dictionary.Add
IMethodSymbol _dictionaryAddMethodSymbol = ResolveMethod(semanticModel, typeof(Dictionary<,>), "Add"); // ok
var implementationMethodSymbol = _dictionaryAddMethodSymbol.ContainingType.FindImplementationForInterfaceMember(_idictionaryAddMethodSymbol); // null
}
private ITypeSymbol ResolveType(SemanticModel semanticModel, Type type)
{
string[] names = type.FullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
INamespaceOrTypeSymbol scope = null;
for (int i = 0; i != names.Count(); ++i)
{
string metadataName = names[i];
string name = metadataName;
int index = name.IndexOf('`');
int numberOfGenericTypes = 0;
if (index != -1)
{
string sNumber = name.Substring(index + 1);
if (!int.TryParse(sNumber, out numberOfGenericTypes))
{
return null;
}
name = name.Substring(0, index);
}
IEnumerable<ISymbol> symbols = semanticModel.LookupNamespacesAndTypes(0, scope, name);
if (numberOfGenericTypes != 0)
{
symbols = symbols.Where(s => s.MetadataName == metadataName);
}
if (symbols.Count() == 1)
{
scope = (INamespaceOrTypeSymbol)symbols.First();
}
else
{
scope = null;
break;
}
}
return (ITypeSymbol)scope;
}
public IMethodSymbol ResolveMethod(SemanticModel semanticModel, Type type, string methodName)
{
ITypeSymbol typeSymbol = ResolveType(semanticModel, type);
if (typeSymbol == null)
{
return null;
}
var members = typeSymbol.GetMembers(methodName);
if (members.Length == 1
&& members[0] is IMethodSymbol)
{
return members[0] as IMethodSymbol;
}
return null;
}
}
}
Fixed Example:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
namespace RoslynSymbolsTest
{
public class InterfaceMemberImplementationTest
{
class MyDisposable : IDisposable
{
public void Dispose() { }
}
public void Run()
{
string solutionPath = #"..\..\..\RoslynSymbolsTest.sln";
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionPath).Result;
var project = solution.Projects.Where(p => p.Name == "RoslynSymbolsTest").Single();
var document = project.Documents.Where(d => d.Name == "InterfaceMemberImplementationTest.cs").Single();
var semanticModel = document.GetSemanticModelAsync().Result;
IMethodSymbol idictionaryAddMethodSymbol = ResolveMethod(semanticModel, typeof(IDictionary<,>), "Add");
IMethodSymbol idictionaryAddStringObjectMethodSymbol = ResolveMethod(semanticModel, typeof(IDictionary<,>), new Type[] { typeof(string), typeof(object) }, "Add");
IMethodSymbol idictionaryAddStringStringMethodSymbol = ResolveMethod(semanticModel, typeof(IDictionary<,>), new Type[] { typeof(string), typeof(string) }, "Add");
IMethodSymbol idictionaryGetItemMethodSymbol = ResolveMethod(semanticModel, typeof(IDictionary<,>), "get_Item");
IMethodSymbol dictionaryMethodSymbol = ResolveMethod(semanticModel, typeof(Dictionary<,>), "Add");
IMethodSymbol dictionaryStringObjectMethodSymbol = ResolveMethod(semanticModel, typeof(Dictionary<,>), new Type[] { typeof(string), typeof(object) }, "Add");
IMethodSymbol idisposableDisposeMethodSymbol = ResolveMethod(semanticModel, typeof(IDisposable), "Dispose");
IMethodSymbol myDisposableDisposeMethodSymbol = ResolveMethod(semanticModel, typeof(MyDisposable), "Dispose");
bool result1 = ImplementsInterfaceMember(dictionaryMethodSymbol, idictionaryAddMethodSymbol);
bool result2 = ImplementsInterfaceMember(dictionaryMethodSymbol, idictionaryGetItemMethodSymbol);
bool result3 = ImplementsInterfaceMember(dictionaryStringObjectMethodSymbol, idictionaryAddMethodSymbol);
bool result4 = ImplementsInterfaceMember(dictionaryStringObjectMethodSymbol, idictionaryGetItemMethodSymbol);
bool result5 = ImplementsInterfaceMember(dictionaryStringObjectMethodSymbol, idictionaryAddStringObjectMethodSymbol);
bool result6 = ImplementsInterfaceMember(dictionaryStringObjectMethodSymbol, idictionaryAddStringStringMethodSymbol);
bool result7 = ImplementsInterfaceMember(myDisposableDisposeMethodSymbol, idisposableDisposeMethodSymbol);
}
private static bool ImplementsInterfaceMember(IMethodSymbol implementationMethod, IMethodSymbol interfaceMethod)
{
if (!IsOpenMethod(interfaceMethod))
{
if (implementationMethod.Equals(implementationMethod.ContainingType.FindImplementationForInterfaceMember(interfaceMethod)))
{
return true;
}
}
else
{
INamedTypeSymbol interfaceTypeSymbol = interfaceMethod.ContainingType;
INamedTypeSymbol interfaceConstructedFromTypeSymbol = interfaceTypeSymbol.ConstructedFrom;
INamedTypeSymbol implementationTypeSymbol = implementationMethod.ContainingType;
var implementedInterfaces = implementationTypeSymbol.AllInterfaces.Where(i => i.ConstructedFrom.Equals(interfaceConstructedFromTypeSymbol));
foreach (var implementedInterface in implementedInterfaces)
{
foreach (var implementedInterfaceMember in implementedInterface.GetMembers(interfaceMethod.Name))
{
if (implementedInterfaceMember.OriginalDefinition.Equals(interfaceMethod))
{
var exactImplementedInterfaceMember = implementationMethod.ContainingType.FindImplementationForInterfaceMember(implementedInterfaceMember);
if (implementationMethod.Equals(exactImplementedInterfaceMember))
{
return true;
}
}
}
}
}
return false;
}
private static bool IsOpenMethod(IMethodSymbol method)
{
bool result = method.OriginalDefinition.Equals(method);
return result;
}
private ITypeSymbol ResolveType(SemanticModel semanticModel, Type type)
{
string[] names = type.FullName.Split(new[] { '.', '+' }, StringSplitOptions.RemoveEmptyEntries);
INamespaceOrTypeSymbol scope = null;
for (int i = 0; i != names.Count(); ++i)
{
string metadataName = names[i];
string name = metadataName;
int index = name.IndexOf('`');
int numberOfGenericTypes = 0;
if (index != -1)
{
string sNumber = name.Substring(index + 1);
if (!int.TryParse(sNumber, out numberOfGenericTypes))
{
return null;
}
name = name.Substring(0, index);
}
IEnumerable<ISymbol> symbols;
if (i == 0)
{
symbols = semanticModel.LookupNamespacesAndTypes(0, scope, name);
}
else
{
symbols = scope.GetMembers(name).Where(m => m.Kind == SymbolKind.Namespace || m.Kind == SymbolKind.NamedType);
}
if (numberOfGenericTypes != 0)
{
symbols = symbols.Where(s => s.MetadataName == metadataName);
}
if (symbols.Count() == 1)
{
scope = (INamespaceOrTypeSymbol)symbols.First();
}
else
{
scope = null;
break;
}
}
return (ITypeSymbol)scope;
}
private ITypeSymbol ResolveType(SemanticModel semanticModel, Type type, params Type[] typeParameters)
{
ITypeSymbol typeSymbol = ResolveType(semanticModel, type);
if (typeSymbol == null)
{
return null;
}
ITypeSymbol[] typeParametersSymbols = new ITypeSymbol[typeParameters.Length];
for (int i = 0; i != typeParameters.Length; ++i)
{
ITypeSymbol typeParameterSymbol = ResolveType(semanticModel, typeParameters[i]);
if (typeParameterSymbol == null)
{
return null;
}
typeParametersSymbols[i] = typeParameterSymbol;
}
INamedTypeSymbol constructedTypeSymbol = ((INamedTypeSymbol)typeSymbol).Construct(typeParametersSymbols);
return constructedTypeSymbol;
}
public IMethodSymbol ResolveMethod(SemanticModel semanticModel, Type type, string methodName)
{
ITypeSymbol typeSymbol = ResolveType(semanticModel, type);
if (typeSymbol == null)
{
return null;
}
var members = typeSymbol.GetMembers(methodName);
if (members.Length == 1
&& members[0] is IMethodSymbol)
{
return members[0] as IMethodSymbol;
}
return null;
}
public IMethodSymbol ResolveMethod(SemanticModel semanticModel, Type type, Type[] typeParameters, string methodName)
{
ITypeSymbol typeSymbol = ResolveType(semanticModel, type, typeParameters);
if (typeSymbol == null)
{
return null;
}
var members = typeSymbol.GetMembers(methodName);
if (members.Length == 1
&& members[0] is IMethodSymbol)
{
return members[0] as IMethodSymbol;
}
return null;
}
}
}
You aren't using generics enough.
IDictionary<,> is an unbound generic type. It is not a concrete type; instead, it can construct concrete types by filling in its generic parameters.
As such, it isn't an interface that can be implemented directly. For example, what if you have a class that implements it twice with two different sets of type parameters?
Instead, you need to call Construct() to build a concrete type with type parameters that match your implementing class.

Categories