I have an XML file from which dynamically creates a JSON. The last entry in each row should get a value when the JSON is finished.
However when the JSON is finished only the last entry from the last row has a value. I guess its overwriting since I need to recreate every entry for the right JSON structure.
I split the Group of each row and each point is a JObject except the one with "[0]", thats a JArray.
Has anyone an idea how I can avoid overwriting my JObjects? Thanks.
My XML
My JSON
private static void GenerateElement(JObject parent, string path, string value)
{
var parts = path.Split('.');
foreach (var item in parts)
{
var partsCount = parts.Count();
var currentElement = item;
if (partsCount > 1)
{
path = path.Remove(0, item.Length);
if (path.StartsWith("."))
{
path = path.Remove(0, 1);
}
else if (path.EndsWith("."))
{
path = path.Remove(path.Length - 1, 1);
}
parts = path.Split('.');
}
var nextElementPath = path;
var elementArrayName = currentElement;
if (currentElement.IndexOf("[0]") >= 0)
{
elementArrayName = currentElement.Substring(0, currentElement.IndexOf("[0]"));
}
else
{
parent[currentElement] = new JObject();
}
if (partsCount > 1)
{
if (parent[nextElementPath] == null)
{
if (parent[elementArrayName] == null)
{
parent[elementArrayName] = new JArray();
(parent[elementArrayName] as JArray).Add(new JObject());
}
}
if (parent[elementArrayName] is JArray)
{
parent = parent[elementArrayName][0] as JObject;
}
else
{
parent = parent[currentElement] as JObject;
}
}
else
{
parent[currentElement] = value;
}
}
}
Related
I'm trying to extract a record from a JSON array but it doesn't seem to work.
Here my code :
DataTable table = ConvertJsonToDatatable(responseBody);
System.Windows.Forms.MessageBox.Show(table.Columns[1].ToString(), "transformation");
The MessageBox isn't showing. I have checked responseBody and the variable isn't empty at all.
Here the structure of this variable (and the JSON array rear)
{
"data":
[
[
1651217520000,
1.0562,
1.0562,
1.056,
1.0561,
0,
0
],
[
1651217580000,
1.0561,
1.0563,
1.0561,
1.0561,
0,
0
]
],
"events": null
}
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
var jsonArray = new JArray();
foreach (JObject row in linqArray.Children<JObject>())
{
var createRow = new JObject();
foreach (JProperty column in row.Properties())
{
// Only include JValue types
if (column.Value is JValue)
{
createRow.Add(column.Name, column.Value);
}
}
jsonArray.Add(createRow);
}
return JsonConvert.DeserializeObject<DataTable>(jsonArray.ToString());
}
Does anyone have an idea of how to extract/pick one value from this array (which is a string in my code) ?
Have a nice week end everyone and thanks in advance
you have to fix table creating code
public static DataTable ConvertJsonToDatatable(string jsonString)
{
var jsonLinq = JObject.Parse(jsonString);
// Find the first array using Linq
var linqArray = jsonLinq.Descendants().Where(x => x is JArray).First();
//or maybe this would be enough
var linqArray = JObject.Parse(jsonString)["data"];
var jsonArray = new JArray();
foreach (var row in (JArray)linqArray)
{
var createdRow = new JObject();
var i = 0;
foreach (var item in row)
{
i++;
createdRow.Add("col" + i.ToString(), (string)item);
}
jsonArray.Add(createdRow);
}
return jsonArray.ToObject<DataTable>();
}
how to use
DataTable table = ConvertJsonToDatatable(responseBody);
string val = table.Rows[0].Field<string>("col2");
System.Windows.Forms.MessageBox.Show(val, "transformation");
i probably would be marked negative, but i try to explain how it looks like inside. i made example to show tow to get back list of arrays it might visualize for you.
void Main()
{
string json = "{\"data\":[[1651217520000,1.0562,1.0562,1.056,1.0561,0,0],[1651217580000,1.0561,1.0563,1.0561,1.0561,0,0]],\"events\":null}";
var obj = JObject.Parse(json);
foreach (JToken token in obj.FindTokens("data"))
{
foreach (JArray row in JArray.Parse(token.ToString()))
{
row.Dump();
row[0].Dump("first element");
}
}
}
public static class JsonExtensions
{
public static List<JToken> FindTokens(this JToken containerToken, string name)
{
List<JToken> matches = new List<JToken>();
FindTokens(containerToken, name, matches);
return matches;
}
private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Name == name)
{
matches.Add(child.Value);
}
FindTokens(child.Value, name, matches);
}
}
else if (containerToken.Type == JTokenType.Array)
{
foreach (JToken child in containerToken.Children())
{
FindTokens(child, name, matches);
}
}
}
}
result would be an array of jarray
so you can build you DataTable rows
.Dump() is a left over from linqpad. Good tool. Same as console.write
I want to traverse a Binary Tree vertically. And I found a working code in Geeks for Geeks in C++. I want to convert it into C# but I am unable to do so. Please guide me.
Below is my attempt:
// we always need the address of the Root Node come what may!
public class BstNode
{
public int data { get; set; }
public BstNode left { get; set; }
public BstNode right { get; set; }
public BstNode(int value) => data = value;
}
public class BstTree
{
// For BST
public BstNode Insert(BstNode root, int data)
{
if (root == null)
{
root = new BstNode(data);
root.left = null;
root.right = null;
}
else if (data > root.data)
root.right = Insert(root.right, data);
else root.left = Insert(root.left, data);
return root;
}
// PROBLEM IN BELOW CODE
public void VerticalOrderTraverse(BstNode root)
{
// Base case
if (root == null)
return;
// Create a map and store vertical oder in
Dictionary<int, List<int>> dict = new Dictionary<int, List<int>>();
int hd = 0;
// Create queue to do level order traversal.
// Every item of queue contains node and
// horizontal distance.
Queue<Tuple<BstNode, int>> que = new Queue<Tuple<BstNode, int>>();
que.Enqueue(new Tuple<BstNode, int>(root, hd));
while (que.Count != 0)
{
// pop from queue front
Tuple<BstNode, int> temp = que.Peek();
que.Dequeue();
hd = temp.Item2;
BstNode node = temp.Item1;
// insert this node's data in vector of hash
dict.Add(hd, new List<int>(node.data)); // CONFUSED HERE
if (node.left != null)
que.Enqueue(new Tuple<BstNode, int>(node.left, hd - 1));
if (node.right != null)
que.Enqueue(new Tuple<BstNode, int>(node.right, hd + 1));
}
foreach (var item in dict)
foreach (var ite in item.Value)
Console.WriteLine(ite);
}
}
class Program
{
public static void Main()
{
BstNode root = null;
BstTree bstTree = new BstTree();
root = bstTree.Insert(root, 10);
root = bstTree.Insert(root, 12);
root = bstTree.Insert(root, 7);
root = bstTree.Insert(root, 8);
root = bstTree.Insert(root, 15);
root = bstTree.Insert(root, 11);
root = bstTree.Insert(root, 6);
bstTree.VerticalOrderTraverse(root);
}
}
Kindly note that I am getting exception in "VerticalOrderTraversal" Method.
This VerticalOrderTraversal is exact replica of Vertical Order Traversal in C++
Exception: Key already exists in dictionary
EDIT:
After adding this check still the Logic does not give correct output
if (dict.ContainsKey(hd))
dict[hd].Add(node.data);
else dict.Add(hd, new List<int>(node.data));
Where you have this:
dict.Add(hd, new List<int>(node.data));
The original has this:
m[hd].push_back(node->key);
Now when we look up in documentation what the operator std::map::operator[] does, we find that
If k matches the key of an element in the container, the function returns a reference to its mapped value.
and importantly,
If k does not match the key of any element in the container, the function inserts a new element with that key
The indexer of Dictionary<TKey, TValue>.Item has the same capability ("a set operation creates a new element with the specified key"), but whereas in C++, this implies construction of a new vector as the value of the new element, C# does not create an instance of List<int> for us, so a simple solution could be:
if (dict.ContainsKey(hd))
dict[hd].Add(node.data);
else dict.Add(hd, new List<int>(node.data));
/// <summary>
/// We must calculate horizontal distance.
/// Each node along with its hd shall be queued.
/// Add hd and values in one hashset.
/// </summary>
/// <param name="root"></param>
public void VerticalOrderTraversal(Node<T> root)
{
if (root == null)
return;
int hd = 0;
Queue<Tuple<Node<T>,int>> q = new Queue<Tuple<Node<T>,int>>();
Dictionary<int, HashSet<T>> ht = new Dictionary<int, HashSet<T>>();
q.Enqueue(new Tuple<Node<T>, int>(root,hd));
ht[hd] = new HashSet<T>();
ht[hd].Add(root.Data);
while (q.Count > 0)
{
Tuple<Node<T>, int> current = q.Peek();
q.Dequeue();
if (current.Item1.leftNode != null)
{
if (!ht.ContainsKey(current.Item2 -1))
{
ht[current.Item2 - 1] = new HashSet<T>();
ht[current.Item2 - 1].Add(current.Item1.leftNode.Data);
}
else
{
ht[current.Item2 - 1].Add(current.Item1.leftNode.Data);
}
q.Enqueue(new Tuple<Node<T>, int>(current.Item1.leftNode, current.Item2 - 1));
}
if (current.Item1.rightNode != null)
{
if (!ht.ContainsKey(current.Item2 + 1))
{
ht[current.Item2 + 1] = new HashSet<T>();
ht[current.Item2 + 1].Add(current.Item1.rightNode.Data);
}
else
{
ht[current.Item2 + 1].Add(current.Item1.rightNode.Data);
}
q.Enqueue(new Tuple<Node<T>, int>(current.Item1.rightNode, current.Item2 + 1));
}
}
foreach (int key in ht.Keys)
{
foreach (T data in ht[key])
{
Console.WriteLine("Vertical Level " + key + " value " + data);
}
}
}
there are few changes I made to the original code:
If the level (hd) already exists in the Dict, we need to add the node to the end of the list not to insert a new tuple of level and list. so I added the if statement [if (dict.ContainsKey(hd))]
better use a sorted dictionary so that print will be from the least level not from the first level that was inserted.
In the original code, the list was created while inserting into the Dict, that was an issue because the node.data would be taken as the capacity of the list not as a data in the list.
public void VerticalOrderTraverse(BstNode root)
{
// Base case
if (root == null)
return;
// Create a map and store vertical oder in
SortedDictionary<int, List<int>> dict = new SortedDictionary<int, List<int>>(); //used Sorted Dictionary
int hd = 0;
Queue<Tuple<BstNode, int>> que = new Queue<Tuple<BstNode, int>>();
que.Enqueue(new Tuple<BstNode, int>(root, hd));
while (que.Count != 0)
{
Tuple<BstNode, int> temp = que.Peek();
que.Dequeue();
hd = temp.Item2;
BstNode node = temp.Item1;
if (dict.ContainsKey(hd)) //No need to try creating a new list, add it to the existing
dict[hd].Add(node.data);
else
{
dict.Add(hd, new List<int>());
dict[hd].Add(node.data);
}
if (node.left != null)
que.Enqueue(new Tuple<BstNode, int>(node.left, hd - 1));
if (node.right != null)
que.Enqueue(new Tuple<BstNode, int>(node.right, hd + 1));
}
foreach (var item in dict)
foreach (var ite in item.Value)
Console.WriteLine(ite);
}
}
}
}
public class Solution {
public IList<IList<int>> result = new List<IList<int>>();
public SortedDictionary<int, List<int>> IndexingItems = new SortedDictionary<int, List<int>>();
public IList<IList<int>> VerticalTraversal(TreeNode root)
{
if(root == null) return result;
Queue<(int, TreeNode)> queue = new Queue<(int, TreeNode)>();
int hDis = 0;
queue.Enqueue((hDis, root));
while(queue.Count != 0)
{
(int, TreeNode) qTop = queue.Peek();
queue.Dequeue();
hDis = qTop.Item1;
TreeNode current = qTop.Item2;
AddItemToDictionary(hDis, current);
if(current.left != null)
{
queue.Enqueue((hDis - 1, current.left));
}
if(current.right != null)
{
queue.Enqueue((hDis + 1, current.right));
}
}
foreach(var item in IndexingItems)
{
var value = item.Value as List<int>;
result.Add(value);
}
return result;
}
public void AddItemToDictionary(int hDis, TreeNode node)
{
if(IndexingItems.ContainsKey(hDis))
{
IndexingItems[hDis].Add(node.val);
// Console.WriteLine($"Updating {hDis} value");
}
else
{
// Console.WriteLine($"Adding new item {hDis} to Dictionary with {node.val} value");
IndexingItems.Add(hDis, new List<int>(){ node.val });
}
}
}
I tried with this approach. But I am not sure, why there is a few mismatch in the order of the data for same nodes.
I am trying to return that data from the foreach "hostedId" Can someone help?
public static string GetHostedRecordSet()
{
var request = new ListHostedZonesRequest()
{
MaxItems = "1"
};
var list = client.ListHostedZones(request);
foreach (var hostedId in list.HostedZones)
{
Console.WriteLine("\n Hosted ID is:");
Console.Write(hostedId.Id);
}
return hostedId;
}
It depends. If you want to return the first element:
return list.HostedZones.First().Id; // Not in a loop!
If you want to return several items, change the signature of the method:
public static IEnumerable<string> GetHostedRecordSet()
{
var request = new ListHostedZonesRequest()
{
MaxItems = "1"
};
var list = client.ListHostedZones(request);
return list.HostedZones
.Select(z => z.Id);
}
If you want to return all values as a single string you can concatenate them with a delimiter, such as ',':
public static string GetHostedRecordSet()
{
var request = new ListHostedZonesRequest()
{
MaxItems = "1"
};
var list = client.ListHostedZones(request);
StringBuilder result = new StringBuilder();
foreach (var hostedId in list.HostedZones)
{
result.Append(hostedId.Id).Append(",");
}
return result.ToString(0, Math.Max(0, result.Length - 1);
}
I am having an issue where i am looping through multiple of the same nodes in an xml bill. As i was looping through the values and merging them accordingly I didnt realize that the datatype was a string. I need some help merging/summing up numbers that are strings. the loop contains all of the values included in the node but i am only showing the portion i need help with.
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
saBillDetail.TotalServiceUsage = totalSvcNode.InnerText;
}
}
}
This is how i had it and i figured the datatype was a string. this way just concatenates the values together.
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
saBillDetail.TotalServiceUsage += totalSvcNode.InnerText;
}
}
}
then i tried something like this where i am using int.Parse and adding the result back to result. But i am getting an error with the second result in the result = result + int.Parse(totalSvcNode.InnerText); saying it is unassigned variable
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach (XmlNode detailPageNode in detailPageNodes)
{
if (totalSvcUseXMLNodes.Count > 0 && totalSvcUseXMLNodes[0].HasChildNodes)
{
var totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGP_SA_TOTAL_KWH.SERVICE_ACCOUNT_STATEMENT");
if (totalSvcNode == null)
{
totalSvcNode = totalSvcUseXMLNodes[0].SelectSingleNode("IRBILGU_US_KWH_USAGE.USAGE");
}
int result;
//saBillDetail.TotalServiceUsage += totalSvcNode.InnerText;
result = result + int.Parse(totalSvcNode.InnerText);
saBillDetail.TotalServiceUsage = result.ToString();
}
}
}
I am stuck with the logic since its not two different TotalServiceUsage i am adding up together. Its the same TotalServiceUsage added up for every node found. I am a pretty new programmer and any help would be appreciated.
In your last example int result needs to be initialized outside the for loop:
int result =0;
If TotalServiceUsage is an int you can just use:
saBillDetail.TotalServiceUsage += int.Parse(totalSvcNode.InnerText);
I have a JSON file:
{
"abn":"63119059513",
"acn":"119059513",
"business_structure":"Private Company",
"ngr_number":"1231231",
"cbh_number":"1231231",
"main_name":"Brickworks Building Products Pty Ltd",
"trading_name":"Brickworks",
"other_trading_names":"Austral Bricks",
"directors":[
{
"ID":"12114",
"ae_forms_filled_in_ID":"22739",
"name":"John Smith",
"dob":"1983-10-29",
"address_line_1":"123 Fake Street",
"address_line_2":"",
"address_line_city":"Fakeland",
"address_line_postcode":"2000",
"address_line_state":"New South Wales",
"address_line_country":"Australia",
"order_extract_id":null,
"director_found":null,
"drivers_lic":"",
"home_mortgage":"",
"phone":"",
"mobile":"",
"director_email":"",
"director_title":"Mr",
"director_position":"Director",
"dir_pdf_url":null
}
],
}
I want to determine if the value of any property has a structure of an array. The best I can come up with so far is:
StreamReader streamrr = new StreamReader("C:\\temp\\agfarm_example_udate.json", Encoding.UTF8);
string JSON = streamrr.ReadToEnd();
JObject CWFile = JObject.Parse(JSON);
foreach (JProperty property in CWFile.Properties())
{
// Do something
if (property.Value.ToString().Contains("["))
{
// Do something with the array
JArray items = (JArray)CWFile[property.Name];
foreach (JObject o in items.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
// Do something
}
}
}
}
To determine whether or not a property value has an array, I used the condition:
if (property.Value.ToString().Contains("["))
I'm just wondering if there is a better way of doing this check?
One way to do this is to check the JToken.Type property. Arrays are of type JTokenType.Array:
if (property.Value.Type == JTokenType.Array)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Or, you can just try to cast to JArray:
if (property.Value is JArray)
{
var items = (JArray)property.Value;
// Proceed as before.
}
Both are preferable to checking property.Value.ToString().Contains("[") since a nested property might have an array value, thus causing a bracket to appear somewhere in the ToString() return.
If you want to recursively find every property with an array value, you can introduce an extension method:
public static class JsonExtensions
{
public static IEnumerable<JToken> WalkTokens(this JToken node)
{
if (node == null)
yield break;
yield return node;
foreach (var child in node.Children())
foreach (var childNode in child.WalkTokens())
yield return childNode;
}
}
And then do:
var CWFile = JToken.Parse(JSON)
var arrayProperties = CWFile.WalkTokens().OfType<JProperty>().Where(prop => prop.Value.Type == JTokenType.Array);
public void Traverse(string name, JToken j)
{
foreach (JToken token in j.AsJEnumerable())
if (token.Type == JTokenType.Object)
{
foreach (var pair in token as JObject)
{
string name_ = pair.Key;
JToken child = pair.Value;
Traverse(name, child);
}
}
else if (token.Type == JTokenType.Array) //an array property found
{
foreach (var child in token.Children())
Traverse(((JProperty)j).Name, child);
}
else if (token.Type == JTokenType.Property)
{
var property = token as JProperty; //current level property
Traverse(name, (JContainer)token);
}
else //current level property name & value
{
var nm = "";
var t = "";
if (j is JProperty)
{
nm = ((JProperty)j).Name;
t = Convert.ToString(((JProperty)j).Value);
}
t = Convert.ToString(token);
}
}
Call:
JSON = JObject.Parse(...);
Traverse("", JSON);
To parse and already parsed text for the same reason is not very wise :
if (property.Value.ToString().Contains("["))
Json structure is simple : objects, arrays, properties, values
http://www.json.org/
So array is a wellknown object for json and we are looking for it :
if (token.Type == JTokenType.Array) //an array property