JSON from Adjacency list? - c#

I have an adjacency list like this:
A - A1
A - A2
A - A3
A3 - A31
A31 - A311
A31 - A312
I am trying to obtain the following output:
{
"name": "A",
"children": [{
"name": "A1"
}, {
"name": "A2"
}, {
"name": "A3",
"children": [{
"name": "A31",
"children": [{
"name": "A311"
}, {
"name": "A312"
}]
}]
}]
};
I have a modestly large graph containing 100K links. What is a good way of doing this? I am thinking there is a very elegant recursive way of doing this but am not sure about how to create the JSON string directly.

Something like should work:
static void Main(string[] args)
{
var adjList = new List<Link>
{
new Link("A","A1"),
new Link("A","A2"),
new Link("A","A3"),
new Link("A3","A31"),
new Link("A31","A311"),
new Link("A31","A312"),
};
var rootsAndChildren = adjList.GroupBy(x => x.From)
.ToDictionary(x => x.Key, x => x.Select(y => y.To).ToList());
var roots = rootsAndChildren.Keys
.Except(rootsAndChildren.SelectMany(x => x.Value));
using (var wr = new StreamWriter("C:\\myjson.json"))
{
wr.WriteLine("{");
foreach (var root in roots)
AppendSubNodes(wr, root, rootsAndChildren, 1);
wr.WriteLine("};");
}
}
static void AppendSubNodes(TextWriter wr, string root,
Dictionary<string, List<string>> rootsAndChildren, int level)
{
string indent = string.Concat(Enumerable.Repeat(" ", level * 4));
wr.Write(indent + "\"name\" : \"" + root + "\"");
List<string> children;
if (rootsAndChildren.TryGetValue(root, out children))
{
wr.WriteLine(",");
wr.WriteLine(indent + "\"children\" : [{");
for (int i = 0; i < children.Count; i++)
{
if (i > 0)
wr.WriteLine(indent + "}, {");
AppendSubNodes(wr, children[i], rootsAndChildren, level + 1);
}
wr.WriteLine(indent + "}]");
}
else
{
wr.WriteLine();
}
}
With Link being the following class:
class Link
{
public string From { get; private set; }
public string To { get; private set; }
public Link(string from, string to)
{
this.From = from;
this.To = to;
}
}
Result of the previous code:
{
"name" : "A",
"children" : [{
"name" : "A1"
}, {
"name" : "A2"
}, {
"name" : "A3",
"children" : [{
"name" : "A31",
"children" : [{
"name" : "A311"
}, {
"name" : "A312"
}]
}]
}]
};
EDIT :
If you want to check the existence of graph cycles you can do the following (just after the creation of rootsAndChildren dictionary)
var allNodes = rootsAndChildren.Keys.Concat(rootsAndChildren.SelectMany(x => x.Value)).Distinct();
Func<string, IEnumerable<string>> getSuccessors =
(x) => rootsAndChildren.ContainsKey(x) ? rootsAndChildren[x] : Enumerable.Empty<string>();
var hasCycles = new Tarjan<string>().HasCycle(allNodes, getSuccessors);
With Tarjan being the following class:
// Please note that Tarjan does not detect a cycle due to a node
// pointing to itself. It's pretty trivial to account for that though...
public class Tarjan<T>
{
private class Node
{
public T Value { get; private set; }
public int Index { get; set; }
public int LowLink { get; set; }
public Node(T value)
{
this.Value = value;
this.Index = -1;
this.LowLink = -1;
}
}
private Func<T, IEnumerable<T>> getSuccessors;
private Dictionary<T, Node> nodeMaps;
private int index = 0;
private Stack<Node> stack;
private List<List<Node>> SCC;
public bool HasCycle(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
{
return ExecuteTarjan(nodes, getSuccessors).Any(x => x.Count > 1);
}
private List<List<Node>> ExecuteTarjan(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
{
this.nodeMaps = nodes.ToDictionary(x => x, x => new Node(x));
this.getSuccessors = getSuccessors;
SCC = new List<List<Node>>();
stack = new Stack<Node>();
index = 0;
foreach (var node in this.nodeMaps.Values)
{
if (node.Index == -1)
TarjanImpl(node);
}
return SCC;
}
private IEnumerable<Node> GetSuccessors(Node v)
{
return this.getSuccessors(v.Value).Select(x => this.nodeMaps[x]);
}
private List<List<Node>> TarjanImpl(Node v)
{
v.Index = index;
v.LowLink = index;
index++;
stack.Push(v);
foreach (var n in GetSuccessors(v))
{
if (n.Index == -1)
{
TarjanImpl(n);
v.LowLink = Math.Min(v.LowLink, n.LowLink);
}
else if (stack.Contains(n))
{
v.LowLink = Math.Min(v.LowLink, n.Index);
}
}
if (v.LowLink == v.Index)
{
Node n;
List<Node> component = new List<Node>();
do
{
n = stack.Pop();
component.Add(n);
} while (n != v);
SCC.Add(component);
}
return SCC;
}
}

Related

Convert generic Json with multiple nested array to a Datatable C#

I need help, I need a function that recive a Json and returns a Datatable, any input can come in Json format so I can't use a object.
My problem is when the Json comes with multiple Arrays nested in the response (the Json can have any format, it can come without Arrays or it can come with 1 or more Arrays), any idea how to make this possible? I need the algorithm to be as generic as possible. Thanks
I leave an example input and what I expect to get
public const string ResponseThreeArrays = #"{
""key1"": ""val1"",
""key2"": {
""key2-1"":
[
{
""key2-arr1-1"": ""val2-arr1-1(1)"",
""key2-arr1-2"":
[
{
""key2-arr1-arr2-1"" : ""val2-arr1-arr2-1(1)(1)"",
""key2-arr1-arr2-2"" :
[
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(1)(1)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(1)(1)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(1)(1)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(1)(2)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(1)(2)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(1)(2)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(1)(3)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(1)(3)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(1)(3)""
}
],
""key2-arr1-arr2-3"" : ""val2-arr1-arr2-3(1)(1)""
},
{
""key2-arr1-arr2-1"" : ""val2-arr1-arr2-1(1)(2)"",
""key2-arr1-arr2-2"" :
[
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(2)(1)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(2)(1)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(2)(1)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(2)(2)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(2)(2)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(2)(2)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(1)(2)(3)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(1)(2)(3)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(1)(2)(3)""
}
],
""key2-arr1-arr2-3"" : ""val2-arr1-arr2-3(1)(2)""
}
]
},
{
""key2-arr1-1"": ""val2-arr1-1(2)"",
""key2-arr1-2"":
[
{
""key2-arr1-arr2-1"" : ""val2-arr1-arr2-1(2)(1)"",
""key2-arr1-arr2-2"" :
[
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(1)(1)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(1)(1)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(1)(1)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(1)(2)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(1)(2)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(1)(2)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(1)(3)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(1)(3)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(1)(3)""
}
],
""key2-arr1-arr2-3"" : ""val2-arr1-arr2-3(2)(1)""
},
{
""key2-arr1-arr2-1"" : ""val2-arr1-arr2-1(2)(2)"",
""key2-arr1-arr2-2"" :
[
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(2)(1)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(2)(1)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(2)(1)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(2)(2)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(2)(2)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(2)(2)""
},
{
""key2-arr1-arr2-arr3-1"" : ""val2-arr1-arr2-arr3-1(2)(2)(3)"",
""key2-arr1-arr2-arr3-2"" : ""val2-arr1-arr2-arr3-2(2)(2)(3)"",
""key2-arr1-arr2-arr3-3"" : ""val2-arr1-arr2-arr3-3(2)(2)(3)""
}
],
""key2-arr1-arr2-3"" : ""val2-arr1-arr2-3(2)(2)""
}
]
}
],
""key2-2"" : ""val2-2""
},
""key3"": ""val3""
}";
And this is what I expect obtain:
Datatable expected
Use Cinchoo ETL - an open source library to do such conversion easily. (Not sure, this meets your requirement)
using (var r = ChoJSONReader.LoadText(ResponseThreeArrays)
.ErrorMode(ChoErrorMode.IgnoreAndContinue)
)
{
var dt = r.FlattenBy("key2", "key2-1", "key2-arr1-2", "key2-arr1-arr2-2").AsDataTable();
dt.Print();
}
Output:
key1,key3,key2-2,key2-arr1-1,key2-arr1-arr2-1,key2-arr1-arr2-3,key2-arr1-arr2-arr3-1,key2-arr1-arr2-arr3-2,key2-arr1-arr2-arr3-3
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(1),val2-arr1-arr2-3(1)(1),val2-arr1-arr2-arr3-1(1)(1)(1),val2-arr1-arr2-arr3-2(1)(1)(1),val2-arr1-arr2-arr3-3(1)(1)(1)
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(1),val2-arr1-arr2-3(1)(1),val2-arr1-arr2-arr3-1(1)(1)(2),val2-arr1-arr2-arr3-2(1)(1)(2),val2-arr1-arr2-arr3-3(1)(1)(2)
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(1),val2-arr1-arr2-3(1)(1),val2-arr1-arr2-arr3-1(1)(1)(3),val2-arr1-arr2-arr3-2(1)(1)(3),val2-arr1-arr2-arr3-3(1)(1)(3)
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(2),val2-arr1-arr2-3(1)(2),val2-arr1-arr2-arr3-1(1)(2)(1),val2-arr1-arr2-arr3-2(1)(2)(1),val2-arr1-arr2-arr3-3(1)(2)(1)
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(2),val2-arr1-arr2-3(1)(2),val2-arr1-arr2-arr3-1(1)(2)(2),val2-arr1-arr2-arr3-2(1)(2)(2),val2-arr1-arr2-arr3-3(1)(2)(2)
val1,val3,val2-2,val2-arr1-1(1),val2-arr1-arr2-1(1)(2),val2-arr1-arr2-3(1)(2),val2-arr1-arr2-arr3-1(1)(2)(3),val2-arr1-arr2-arr3-2(1)(2)(3),val2-arr1-arr2-arr3-3(1)(2)(3)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(1),val2-arr1-arr2-3(2)(1),val2-arr1-arr2-arr3-1(2)(1)(1),val2-arr1-arr2-arr3-2(2)(1)(1),val2-arr1-arr2-arr3-3(2)(1)(1)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(1),val2-arr1-arr2-3(2)(1),val2-arr1-arr2-arr3-1(2)(1)(2),val2-arr1-arr2-arr3-2(2)(1)(2),val2-arr1-arr2-arr3-3(2)(1)(2)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(1),val2-arr1-arr2-3(2)(1),val2-arr1-arr2-arr3-1(2)(1)(3),val2-arr1-arr2-arr3-2(2)(1)(3),val2-arr1-arr2-arr3-3(2)(1)(3)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(2),val2-arr1-arr2-3(2)(2),val2-arr1-arr2-arr3-1(2)(2)(1),val2-arr1-arr2-arr3-2(2)(2)(1),val2-arr1-arr2-arr3-3(2)(2)(1)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(2),val2-arr1-arr2-3(2)(2),val2-arr1-arr2-arr3-1(2)(2)(2),val2-arr1-arr2-arr3-2(2)(2)(2),val2-arr1-arr2-arr3-3(2)(2)(2)
val1,val3,val2-2,val2-arr1-1(2),val2-arr1-arr2-1(2)(2),val2-arr1-arr2-3(2)(2),val2-arr1-arr2-arr3-1(2)(2)(3),val2-arr1-arr2-arr3-2(2)(2)(3),val2-arr1-arr2-arr3-3(2)(2)(3)
Sample fiddle: https://dotnetfiddle.net/wnacwN
Disclaimer: I'm the author of this library.
I leave here what I have so far, #Cinchoo
public DataTable MapJson(string json)
{
var jo = JToken.Parse(json);
var stringReader = new StringReader(MakeConfigString(jo));
var serializer = new XmlSerializer(typeof(DataTableConfig));
var config = (DataTableConfig)serializer.Deserialize(stringReader);
var dataTable = new DataTable();
foreach (var p in config.Property)
{
dataTable.Columns.Add(p.Name);
}
var regex = new Regex(Regex.Escape("*"));
var count = 0;
if (jo is JArray)
{
count = jo.Count();
}
else if (jo is JObject jsonLinq)
{
count = jsonLinq.Descendants().FirstOrDefault(d => d is JArray)?.Count() ?? 1;
}
var dataCollection = Enumerable.Range(0, count)
.Select(x => config.Property
.Select(prop => new KeyValuePair<string, List<string>>
(
prop.Name,
jo.SelectTokens(regex.Replace(prop.Path, x.ToString(), 1)).Select(c => c.Value<string>()).ToList())
));
foreach (var data in dataCollection)
{
var countMax = data.Select(x => x.Value).Max(x => x.Count);
for (var i = 0; i < countMax; i++)
{
var innerList = new List<object>();
foreach (var prop in config.Property)
{
var currentData = data.Where(x => x.Key == prop.Name).SelectMany(x => x.Value).ToList();
if (currentData.Any())
{
if (i >= currentData.Count)
{
innerList.Add(currentData.Last());
}
else
{
innerList.Add(currentData[i]);
}
}
else
{
innerList.Add("");
}
}
dataTable.Rows.Add(innerList.ToArray());
}
}
return dataTable;
}
private string MakeConfigString(JToken json)
{
var result = #"<DataTableConfig Name='DataTable'>";
foreach (var col in this.ApiConnectorsColumns)
{
var path = json is JArray ? col.PathNode.Replace(".Columns", "[*]") : col.PathNode.Remove(0, 9);
var temp = $"<Property Name='{col.ColumnName}' Path='{path}'/>";
result += temp;
}
result += "</DataTableConfig>";
return result;
}
[XmlRoot(ElementName = "Property")]
public class Property
{
[XmlAttribute(AttributeName = "Name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "Path")]
public string Path { get; set; }
}
[XmlRoot(ElementName = "DataTableConfig")]
public class DataTableConfig
{
[XmlElement(ElementName = "Property")]
public List<Property> Property { get; set; }
[XmlAttribute(AttributeName = "Name")]
public string Name { get; set; }
}

Using JSON.Net / C# to read root value in json data

Sorry for the nube-like question, but having been retired for sometime I find myself forgetting some things.
Given this sample json string:
{
"range": [
{ "num": 0 },
{ "num": 1 },
{ "num": 2 },
{ "num": 3 },
{ "num": 4 },
{ "num": 5 },
{ "num": 6 },
{ "num": 7 },
{ "num": 8 },
{ "num": 9 }
],
"friends": [
{
"id": 0,
"name": "Christian Cruz"
},
{
"id": 1,
"name": "Hunter Moon"
},
{
"id": 2,
"name": "Holden Gentry"
}
]
}
I would like to be able to read the root value ("range" and "friends" in this case) for each line in the data, then parse the remaining values.
void Main()
{
var json = File.ReadAllText(#"c:\data\sample.json");
JObject obj = JObject.Parse(json);
foreach(JProperty child in obj.Children())
{
}
}
Where I bogged down is as I iterate through the children collection (foreach(JProperty child ...) I can read the items in the array (e.g. "num", "id" and "name") but I am unable to read the root values (e.g. "range" and "friends")
Any help you could lend an old man would be very much appreciated.
It's much easier to deserialise it into C# objects, for example:
public class RootObject
{
public List<Range> range { get; set; }
public List<Friend> friends { get; set; }
}
public class Range
{
public int num { get; set; }
}
public class Friend
{
public int id { get; set; }
public string name { get; set; }
}
Now you can use deserialise like this:
var root = JsonConvert.DeserializeObject<RootObject>(json);
And use the data:
foreach (var range in root.Range)
{
//Do stuff
}
foreach (var friend in root.Friends)
{
//Do stuff
}
You can use SelectTokens , put the json in list and then iterate through JProperty.
var files = JObject.Parse(YourJson);
var recList = files.SelectTokens("$").ToList();
foreach (JProperty item in recList.Children())
{
var key = item.Name.ToString(); //store the root item here
var value = item.Value.ToString();
//Do your stuffs
}
Using this small recursive function below you can unpack JSON to see properties as well as values without making any classes
static void Main(string[] args)
{
var json = "{\"range\":[{\"num\":0},{\"num\":1},{\"num\":2},{\"num\":3},{\"num\":4},{\"num\":5},{\"num\":6},{\"num\":7},{\"num\":8},{\"num\":9}],\"friends\":[{\"id\":0,\"name\":\"Christian Cruz\"},{\"id\":1,\"name\":\"Hunter Moon\"},{\"id\":2,\"name\":\"Holden Gentry\"}]}";
JObject obj = JObject.Parse(json);
void UnpackJson(JToken jobj, int indent)
{
if (jobj == null)
return;
var name = (jobj as JProperty)?.Name;
if (name != null)
{
Console.Write(new string(' ', indent) + name + " :\n");
indent += 4;
}
foreach (var child in jobj.Children())
{
var chname = (child as JProperty)?.Name;
if (chname != null)
Console.Write(new string(' ', indent) + chname + " : ");
var value = (child as JProperty)?.Value;
if (child.Values().Count() > 1)
{
if (chname != null || name != null)
Console.WriteLine();
IEnumerable<JToken> jt = (value is JArray) ? child.Values() : child.Children();
foreach (var val in jt)
UnpackJson(val, indent + 4);
}
else
{
if (value != null)
Console.WriteLine(value);
}
}
}
UnpackJson(obj, 0);
Console.Read();
}
Output:
range :
num : 0
num : 1
num : 2
num : 3
num : 4
num : 5
num : 6
num : 7
num : 8
num : 9
friends :
id : 0
name : Christian Cruz
id : 1
name : Hunter Moon
id : 2
name : Holden Gentry

How can I serialize object without some property names

I want to generate the following json output.I tried to serialize object but as you see from output there is no json string name for "data" array, If its serialized it will add the property name to array. Please suggest a solution for this and optimize it in generic way.This is dummy data orginal Data will be dynamic.
[
{
"data": [[0, -74984809.4603082], [1, -1547043466.02543], [2, 1197200673.15602], [3, -1329017076.87315]],
"label": "Sine Wave"
},
{
"data": [[0, -16009669.0446502], [1, -1349266386.03401], [2, 1730901576.10012], [3, -48303271.446511]],
"label": "Cosine Wave"
},
{
"data": [[0, -22114801.1769572], [1, 1737206285.30856], [2, 1280894300.93367], [3, -14969166.212896]],
"label": "Sine2 Wave"
},
{
"data": [[0, -236097551.627018], [1, 8971833.78377865], [2, -805237017.202245], [3, -28865332.2392996]],
"label": "Cosine2 Wave"
}
]
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Web.Services;
using System.Web.UI;
using Microsoft.Ajax.Utilities;
namespace D3WebApp
{
public partial class _Default : Page
{
public class Datum
{
public int No { get; set; }
public double Value { get; set; }
}
public class LineGraphData
{
public Datum Data { get; set; }
public string Label { get; set; }
}
public class GraphData
{
public Datum[] Data { get; set; }
public string Label { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
[WebMethod]
public static string GetData()
{
return GetDataForChart(); ;
}
private static string GetDataForChart()
{
var r1 = new Random();
var r2 = new Random();
var r3 = new Random();
var r4 = new Random();
var datalist = new List<LineGraphData>();
for (var i = 0; i < 4; i++)
{
datalist.Add(new LineGraphData()
{
Data = new Datum()
{
No = i,
Value = r1.Next() * Math.Sin(r2.Next() + i / (10 * (r4.Next() + .5)))
},
Label = "Sine Wave"
});
datalist.Add(new LineGraphData()
{
Data = new Datum()
{
No = i,
Value = r2.Next() * Math.Sin(r3.Next() + i / (10 * (r3.Next() + .5)))
},
Label = "Cosine Wave"
});
datalist.Add(new LineGraphData()
{
Data = new Datum()
{
No = i,
Value = r3.Next() * Math.Sin(r1.Next() + i / (10 * (r2.Next() + .5)))
},
Label = "Sine2 Wave"
});
datalist.Add(new LineGraphData()
{
Data = new Datum()
{
No = i,
Value = r4.Next() * Math.Sin(r4.Next() + i / (10 * (r1.Next() + .5)))
},
Label = "Cosine2 Wave"
});
}
var sb = new StringBuilder();
var valuelist = new List<GraphData>();
//want to remove this or make it cleaner
foreach (var result in datalist.DistinctBy(x => x.Label).Select(x => x.Label))
{
var childItems = datalist.
Where(x => x.Label == result).Select(x => x.Data).ToList();
valuelist.Add(new GraphData() { Label = result, Data = childItems.ToArray() });
sb.Append(#"{""data"":");
sb.Append("[");
foreach (var value in childItems)
{
sb.Append(string.Format("[{0},{1}],", value.No, value.Value));
}
var index = sb.ToString().LastIndexOf(',');
if (index >= 0)
sb.Remove(index, 1);
sb.Append("],");
sb.Append(string.Format(#"""{0}"":""{1}""", "label", result));
sb.Append("},");
}
var testdata = new JavaScriptSerializer().Serialize(valuelist);
var datastr = "[" + sb.ToString().Trim(',') + "]";
return datastr;
}
}
}
Please use Newtonsoft.JSON and Linq method GroupBy here:
var datalistDynamic = datalist.GroupBy(it => it.Label).Select(
dataItemGrouped => new
{
Label = dataItemGrouped.Key,
Data = dataItemGrouped.Select(dataItem => new[] {dataItem.Data.No, dataItem.Data.Value})
}).ToArray();
var datastr = JsonConvert.SerializeObject(datalistDynamic);
return datastr;
and this code can be removed:
var sb = new StringBuilder();
var valuelist = new List<GraphData>();
//want to remove this or make it cleaner
foreach (var result in datalist.DistinctBy(x => x.Label).Select(x => x.Label))
{
var childItems = datalist.
Where(x => x.Label == result).Select(x => x.Data).ToList();
valuelist.Add(new GraphData() { Label = result, Data = childItems.ToArray() });
sb.Append(#"{""data"":");
sb.Append("[");
foreach (var value in childItems)
{
sb.Append(string.Format("[{0},{1}],", value.No, value.Value));
}
var index = sb.ToString().LastIndexOf(',');
if (index >= 0)
sb.Remove(index, 1);
sb.Append("],");
sb.Append(string.Format(#"""{0}"":""{1}""", "label", result));
sb.Append("},");
}
var testdata = new JavaScriptSerializer().Serialize(valuelist);
var datastr = "[" + sb.ToString().Trim(',') + "]";
If you want to ignore property, please use attribute JsonIgnore, for example
public class GraphData
{
public Datum[] Data { get; set; }
[JsonIgnore]
public string Label { get; set; }
}
If you want to have Json output after serialization like posted in your question. You should follow this class hierarchy:
public class Sample
{
[JsonProperty("data")]
public List<List<double>> Data { get; set; }
[JsonProperty("label")]
public string Label { get; set; }
}
And then you should create array of samples which should be serialized via Json.Net
var samples = new List<Sample>();
samples.Add(new Sample()
{
Data = new List<List<double>>()
{
new List<double> { 0, 6.48 },
new List<double> { 1, 6.43 }
//...other values
},
Label = "Port-1"
};
string json = JsonConvert.SerializeObject(samples);

Using MongoDB C# driver find and update a node from parent children hierarchy

I have a hierarchical category document, like parent - Children - Children and so on....
{
id: 1,
value: {
}Children: [{
id: 2,
value: {
}Children: [{
id: 3,
value: {
}Children: [{
id: 4,
value: {
}Children: [{
id: 5,
value: {
}Children: [{
id: 6,
value: {
}Children: [{
id: 7,
value: {
}Children: []
}]
}]
}]
}]
}]
}]
}
In such documents, using MongoDB C# driver, how can I find a node where Id = x
I tried something like this
var filter = Builders<Type>.Filter.Eq(x => x.Id, 3);
var node = mongoDbRepository.GetOne(filter) ??
mongoDbRepository.GetOne(Builders<Type>.Filter.Where(x => x.Children.Any(c=>c.Id == 3)));
But this covers only two levels. In my example, I have 7 levels and I don't have a restriction on depth of level
Once I find that node I need to update that node.
MongoDB Documentation talks about hierarchical documents, but doesn't cover my scenario.
In your situation if you
don't have a restriction on depth of level
you can`t create update query. You must change schema for store data:
https://docs.mongodb.com/v3.2/tutorial/model-tree-structures/
If you depth is fixed:
public class TestEntity
{
public int Id { get; set; }
public TestEntity[] Value { get; set; }
}
class Program
{
static void Main()
{
const string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("TestEntities");
var collection = db.GetCollection<TestEntity>("Entities");
collection.InsertOne(CreateTestEntity(1, CreateTestEntity(2, CreateTestEntity(3, CreateTestEntity(4)))));
const int selctedId = 3;
var update = Builders<TestEntity>.Update.AddToSet(x => x.Value, CreateTestEntity(9));
var depth1 = Builders<TestEntity>.Filter.Eq(x => x.Id, selctedId);
var depth2 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Id == selctedId));
var depth3 = Builders<TestEntity>.Filter.Where(x => x.Value.Any(item => item.Value.Any(item2 => item2.Id == selctedId)));
var filter = depth1 | depth2 | depth3;
collection.UpdateMany(filter, update);
// if you need update document on same depth that you match it in query (for example 3 as selctedId),
// you must make 2 query (bad approach, better way is change schema):
//var testEntity = collection.FindSync(filter).First();
//testEntity.Value[0].Value[0].Value = new[] {CreateTestEntity(9)}; //todo you must calculate depth what you need in C#
//collection.ReplaceOne(filter, testEntity);
}
private static TestEntity CreateTestEntity(int id, params TestEntity[] values)
{
return new TestEntity { Id = id, Value = values };
}
}
There seems to be something wrong in your example document. If the parent has 3 fileds: _id ,id and value, the example should be
{
"_id" : ObjectId("581bce9064989cce81f2b0c1"),
"id" : 1,
"value" : {
"Children" : [
{
"id" : 2,
"value" : {
"Children" : [
{
"id" : 3,
"value" : {
"Children" : [
{
"id" : 4,
"value" : {
"Children" : [
{
"id" : 5,
"value" : {
"Children" : [
{
"id" : 6,
"value" : {
"Children" : [
{
"id" : 7,
"value" : {
"Children" : []
}
}
]
}
}
]
}
}
]
}
}
]
}
}
]
}
}
]
}
}
Please try the following function, where target is the document and x is the id number you want to update.
bool FindAndUpdate2(BsonDocument target, int x)
{
BsonValue id, children;
while (true)
{
try
{
if (target.TryGetValue("_id", out children))
{
id = target.GetValue(1);
children = target.GetValue(3);
}
else
{
id = target.GetValue(0);
children = target.GetValue(2);
}
if (id.ToInt32() == x)
{
Update(target); //change as you like
return true; //success
}
else
target = children[0] as BsonDocument;
}
catch (Exception ex)
{
return false; //failed
}
}
}
else if the parent has 4 fileds: _id ,id, value and children the example should be
{
"_id" : ObjectId("581bdd3764989cce81f2b0c2"),
"id" : 1,
"value" : {},
"Children" : [
{
"id" : 2,
"value" : {},
"Children" : [
{
"id" : 3,
"value" : {},
"Children" : [
{
"id" : 4,
"value" : {},
"Children" : [
{
"id" : 5,
"value" : {},
"Children" : [
{
"id" : 6,
"value" : {},
"Children" : [
{
"id" : 7,
"value" : {},
"Children" : []
}
]
}
]
}
]
}
]
}
]
}
]
}
Then you can try this:
bool FindAndUpdate2(BsonDocument target, int x)
{
BsonValue id, children;
while (true)
{
try
{
if (target.TryGetValue("_id", out children))
{
id = target.GetValue(1);
children = target.GetValue(3);
}
else
{
id = target.GetValue(0);
children = target.GetValue(2);
}
if (id.ToInt32() == x)
{
Update(target); //change as you like
return true; //success
}
else
target = children[0] as BsonDocument;
}
catch (Exception ex)
{
return false; //failed
}
}
}
I have a version, which is based on #DmitryZyr 's answer, and uses 2 answer of the question How do I create an expression tree calling IEnumerable<TSource>.Any(...)?. Thanks to Aaron Heusser and Barry Kelly:
class Program
{
#region Copied from Expression.Call question
static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags)
{
int typeArity = typeArgs.Length;
var methods = type.GetMethods()
.Where(m => m.Name == name)
.Where(m => m.GetGenericArguments().Length == typeArity)
.Select(m => m.MakeGenericMethod(typeArgs));
return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null);
}
static bool IsIEnumerable(Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
static Type GetIEnumerableImpl(Type type)
{
// Get IEnumerable implementation. Either type is IEnumerable<T> for some T,
// or it implements IEnumerable<T> for some T. We need to find the interface.
if (IsIEnumerable(type))
return type;
Type[] t = type.FindInterfaces((m, o) => IsIEnumerable(m), null);
Debug.Assert(t.Length == 1);
return t[0];
}
static Expression CallAny(Expression collection, Expression predicateExpression)
{
Type cType = GetIEnumerableImpl(collection.Type);
collection = Expression.Convert(collection, cType); // (see "NOTE" below)
Type elemType = cType.GetGenericArguments()[0];
Type predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool));
// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>)
MethodInfo anyMethod = (MethodInfo)
GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType },
new[] { cType, predType }, BindingFlags.Static);
return Expression.Call(anyMethod, collection, predicateExpression);
}
#endregion
public class TestEntity
{
public int Id { get; set; }
public TestEntity[] Value { get; set; }
}
private static TestEntity CreateTestEntity(int id, params TestEntity[] values)
{
return new TestEntity { Id = id, Value = values };
}
static void Main(string[] args)
{
const string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("TestEntities");
IMongoCollection<TestEntity> collection = db.GetCollection<TestEntity>("Entities");
collection.InsertOne(CreateTestEntity(1, CreateTestEntity(2, CreateTestEntity(3, CreateTestEntity(4)))));
const int selectedId = 4;
int searchDepth = 6;
// builds the expression tree of expanding x => x.Value.Any(...)
var filter = GetFilterForDepth(selectedId, searchDepth);
var testEntity = collection.Find(filter).FirstOrDefault();
if (testEntity != null)
{
UpdateItem(testEntity, selectedId);
collection.ReplaceOne(filter, testEntity);
}
}
private static bool UpdateItem(TestEntity testEntity, int selectedId)
{
if (testEntity.Id == selectedId)
{
return true;
}
if (UpdateItem(testEntity.Value[0], selectedId))
testEntity.Value[0] = CreateTestEntity(11);
return false;
}
private static FilterDefinition<TestEntity> GetFilterForDepth(int id, int depth)
{
// item
var idEqualsParam = Expression.Parameter(typeof(TestEntity), "item");
// .Id
var idProp = Expression.Property(idEqualsParam, "Id");
// item.Id == id
var idEquals = Expression.Equal(idProp, Expression.Constant(id));
// item => item.Id == id
var idEqualsLambda = Expression.Lambda<Func<TestEntity, bool>>(idEquals, idEqualsParam);
// x
var anyParam = Expression.Parameter(typeof(TestEntity), "x");
// .Value
var valueProp = Expression.Property(anyParam, "Value");
// Expression.Call would not find easily the appropriate .Any((TestEntity x) => x == id)
// .Value.Any(item => item.Id == id)
var callAny = CallAny(valueProp, idEqualsLambda);
// x => x.Value.Any(item => item.Id == id)
var firstAny = Expression.Lambda<Func<TestEntity, bool>>(callAny, anyParam);
return NestedFilter(Builders<TestEntity>.Filter.Eq(x => x.Id, id), firstAny, depth);
}
static int paramIndex = 0;
private static FilterDefinition<TestEntity> NestedFilter(FilterDefinition<TestEntity> actual, Expression<Func<TestEntity, bool>> whereExpression, int depth)
{
if (depth == 0)
{
return actual;
}
// paramX
var param = Expression.Parameter(typeof(TestEntity), "param" + paramIndex++);
// paramX.Value
var valueProp = Expression.Property(param, "Value");
// paramX => paramX.Value.Any(...)
var callLambda = Expression.Lambda<Func<TestEntity, bool>>(CallAny(valueProp, whereExpression), param);
return NestedFilter(Builders<TestEntity>.Filter.Where(whereExpression), callLambda, depth - 1) | actual;
}
}
It's still fixed length depth search, but the depth can be varied dinamically. Just 1 step to upgrade the code to try 1st level, 2nd level, .... And the depth is infinite

Anti XSS Sanitization of IFrames with specific src attribute values in .NET

I'm looking to accomplish the following: sanitize WYSIWIG user-input using either AntiXSS or AntiSamy libraries, however, allow iframe tags which have "src" attribute from particular domains. Is there a way to accomplish this?
I was thinking about parsing it up with Regex somehow and swapping it out "< iframe" for something like {{iframe-begin}} tag and later on swapping it out in the controller logic with "
Thank you.
I also wanted to do some HTML sanitization for one of my WYSISWIG editors.
One aproach is to use the Microsoft Anti-Cross Site Scripting Library. http://msdn.microsoft.com/en-us/library/aa973813.aspx
Another is to create a whitelist parser for HTML.
Here is what I used along with HTML Agility Pack, you can configure the whitelist with tags and allowed atributes:
public static class HtmlSanitizer
{
private static readonly IDictionary Whitelist;
private static List DeletableNodesXpath = new List();
static HtmlSanitizer()
{
Whitelist = new Dictionary<string, string[]> {
{ "a", new[] { "href" } },
{ "strong", null },
{ "em", null },
{ "blockquote", null },
{ "b", null},
{ "p", null},
{ "ul", null},
{ "ol", null},
{ "li", null},
{ "div", new[] { "align" } },
{ "strike", null},
{ "u", null},
{ "sub", null},
{ "sup", null},
{ "table", null },
{ "tr", null },
{ "td", null },
{ "th", null }
};
}
public static string Sanitize(string input)
{
if (input.Trim().Length < 1)
return string.Empty;
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(input);
SanitizeNode(htmlDocument.DocumentNode);
string xPath = HtmlSanitizer.CreateXPath();
return StripHtml(htmlDocument.DocumentNode.WriteTo().Trim(), xPath);
}
private static void SanitizeChildren(HtmlNode parentNode)
{
for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--)
{
SanitizeNode(parentNode.ChildNodes[i]);
}
}
private static void SanitizeNode(HtmlNode node)
{
if (node.NodeType == HtmlNodeType.Element)
{
if (!Whitelist.ContainsKey(node.Name))
{
if (!DeletableNodesXpath.Contains(node.Name))
{
//DeletableNodesXpath.Add(node.Name.Replace("?",""));
node.Name = "removeableNode";
DeletableNodesXpath.Add(node.Name);
}
if (node.HasChildNodes)
{
SanitizeChildren(node);
}
return;
}
if (node.HasAttributes)
{
for (int i = node.Attributes.Count - 1; i >= 0; i--)
{
HtmlAttribute currentAttribute = node.Attributes[i];
string[] allowedAttributes = Whitelist[node.Name];
if (allowedAttributes != null)
{
if (!allowedAttributes.Contains(currentAttribute.Name))
{
node.Attributes.Remove(currentAttribute);
}
}
else
{
node.Attributes.Remove(currentAttribute);
}
}
}
}
if (node.HasChildNodes)
{
SanitizeChildren(node);
}
}
private static string StripHtml(string html, string xPath)
{
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
if (xPath.Length > 0)
{
HtmlNodeCollection invalidNodes = htmlDoc.DocumentNode.SelectNodes(#xPath);
foreach (HtmlNode node in invalidNodes)
{
node.ParentNode.RemoveChild(node, true);
}
}
return htmlDoc.DocumentNode.WriteContentTo(); ;
}
private static string CreateXPath()
{
string _xPath = string.Empty;
for (int i = 0; i < DeletableNodesXpath.Count; i++)
{
if (i != DeletableNodesXpath.Count - 1)
{
_xPath += string.Format("//{0}|", DeletableNodesXpath[i].ToString());
}
else _xPath += string.Format("//{0}", DeletableNodesXpath[i].ToString());
}
return _xPath;
}
}

Categories

Resources