Parse Complex Structure of XML - windows-phone-8

I want to show data before parsing Local XML but don't know how?I tried it by using this
XDocument loadedData = XDocument.Load("People.xml");
var data = from query in loadedData.Descendants("array")
select new Person {
Name = (string)query.Element("string"),
};
listBox1.ItemsSource = data;
but it shows me only one element.
XML is:
<?xml version="1.0" encoding="UTF-8"?>
<dict>
<key>Categories</key>
<array>
<string>Playing baseball</string>
<string>Driving a car</string>
<string>Getting dressed</string>
<string>Eating a hamburger</string>
<string>Tying shoelace</string>
</array>
</dict>
I want to parse all string element.

This should work. You want to iterate the elements within the array element, so we call Descendants("array").Elements(). There's no need to cast query.Value to a string as the value property is returned as a string.
XDocument loadedData = XDocument.Load("People.xml");
var data = (from query in loadedData.Descendants("array").Elements()
select new Person
{
Name = query.Value,
}).ToList();
listBox1.ItemsSource = data;

The Element(System.Xml.Linq.XName) method only returns one element--the first child element with the specified name. Use the Elements(System.Xml.Linq.XName) overload instead, which returns a collection of all elements whose name is the specified name.
Try this code:
XDocument loadedData = XDocument.Load("People.xml");
// Get all the <string /> elements from all th
var data =
from query in loadedData.Descendants("array")
from stringElem in query.Elements("string")
select new Person { Name = (string)stringElem.Value };
listBox1.ItemsSource = data;

Related

U-SQL JsonTuple - How to access specific fields in JSON array

I'm extracting AVRO data which has a JSON field that I need to get values from. The JSON has an array, and I don't know what order the different elements of the array may appear in. How can I target specific node/values?
For example, Filters[0] could be Category one time, but could be AddressType another time.
I'm extracting AVRO data - i.e.
#rs =
EXTRACT date DateTime,
Body byte[]
FROM #input_file
USING new Microsoft.Analytics.Samples.Formats.ApacheAvro.AvroExtractor(#"
...
The Body is JSON that can look like this (but Category is not always Filter[0]. This is a small example; there are 7 different types of "Field"s):
{
""TimeStamp"": ""2019-02-19T15:00:29.1067771-05:00"",
""Filters"": [{
""Operator"": ""eq"",
""Field"": ""Category"",
""Value"": ""Sale""
}, {
""Operator"": ""eq"",
""Field"": ""AddressType"",
""Value"": ""Home""
}
]
}
My U-SQL looks like this, which of course does not always work.
#keyvalues =
SELECT JsonFunctions.JsonTuple(Encoding.UTF8.GetString(Body),
"TimeStamp",
"$.Filters[?(#.Field == 'Category')].Value",
"$.Filters[?(#.Field == 'AddressType')].Value"
) AS message
FROM #rs;
#results =
SELECT
message["TimeStamp"] AS TimeStamp,
message["Filters[0].Value"] AS Category,
message["Filters[1].Value"] AS AddressType
FROM #keyvalues;
Although this does not actually answer my question, as a workaround, I modified the Microsoft 'sample' JsonFunctions.JsonTuple method to be able to specify my own key name for extracted values:
/// Added - Prefix a path expression with a specified key. Use key~$e in the expression.
/// eg:
/// JsonTuple(json, "myId~id", "myName~name") -> field names MAP{ {myId, 1 }, {myName, Ed } }
The modified code:
private static IEnumerable<KeyValuePair<string, T>> ApplyPath<T>(JToken root, string path)
{
var keySeparatorPos = path.IndexOf("~");
string key = null;
var searchPath = path;
if (keySeparatorPos > 0) // =0?if just a leading "=", i.e. no key provided, then don't parse out a key.
{
key = path.Substring(0, keySeparatorPos).Trim();
searchPath = path.Substring(keySeparatorPos + 1);
}
// Children
var children = SelectChildren<T>(root, searchPath);
foreach (var token in children)
{
// Token => T
var value = (T)JsonFunctions.ConvertToken(token, typeof(T));
// Tuple(path, value)
yield return new KeyValuePair<string, T>(key ?? token.Path, value);
}
}
For example, I can access vales and name them
#keyvalues =
SELECT JsonFunctions.JsonTuple(Encoding.UTF8.GetString(Body),
"TimeStamp",
"EventName",
"Plan~ $.UrlParams.plan",
"Category~ $.Filters[?(#.Field == 'Category')].Value",
"AddressType~ $.Filters[?(#.Field == 'AddressType')].Value"
) AS message
FROM #rs;
#results =
SELECT
message["TimeStamp"] AS TimeStamp,
message["EventName"] AS EventName,
message["Plan"] AS Plan,
message["Category"] AS Category,
message["AddressType"] AS AddressType
FROM #keyvalues;
(I've not tested to see what would happen if the same Field appears multiple times in the array; That won't happen in my case)

How can I get access to multiple values of nested JSON object?

I try to access to my data json file:
[{"id":1,"name":"Maria","project":[{"id":5,"name":"Animals"},{"id":6,"name":"Cats"}]}
This is my approach:
data[0].name;
But like this I get only the result:
Animals
But I would need the result:
Animals, Cats
You are accessing only the name property of 0th index of project array.
To access all object at a time you need to loop over the array.
You can use Array.map for this.
var data = [{"id":1,"name":"Maria","project":[{"id":5,"name":"Animals"},{"id":6,"name":"Cats"}]}]
var out = data[0].project.map(project => project.name).toString()
console.log(out)
If that's your actual data object, then data[0].name would give you "Maria". If I'm reading this right, though, you want to get all the names from the project array. You can use Array.map to do it fairly easily. Note the use of an ES6 arrow function to quickly and easily take in the object and return its name.
var bigObject = [{"id":1,"name":"Maria","project":[{"id":5,"name":"Animals"},{"id":6,"name":"Cats"}]}];
var smallObject = [{"id":5,"name":"Animals"},{"id":6,"name":"Cats"}];
console.log("Getting the names from the full array/data structure: "+bigObject[0].project.map(obj => obj.name))
console.log("Getting the names from just the project array: "+smallObject.map(obj => obj.name))
EDIT: As per your comment on the other answer, you said you needed to use the solution in this function:
"render": function (data, type, row) {if(Array.isArray(data)){return data.name;}}
To achieve this, it looks like you should use my bottom solution of the first snippet like so:
var data = [{"id":5,"name":"Animals"},{"id":6,"name":"Cats"}];
function render(data, type, row){
if(Array.isArray(data)){
return data.map(obj => obj.name);
}
};
console.log("Render returns \""+render(data)+"\" as an array.");

USql Call data in multidimensional JSON array

I have this JSON file in a data lake that looks like this:
{
"id":"398507",
"contenttype":"POST",
"posttype":"post",
"uri":"http://twitter.com/etc",
"title":null,
"profile":{
"#class":"PublisherV2_0",
"name":"Company",
"id":"2163171",
"profileIcon":"https://pbs.twimg.com/image",
"profileLocation":{
"#class":"DocumentLocation",
"locality":"Toronto",
"adminDistrict":"ON",
"countryRegion":"Canada",
"coordinates":{
"latitude":43.7217,
"longitude":-31.432},
"quadKey":"000000000000000"},
"displayName":"Name",
"externalId":"00000000000"},
"source":{
"name":"blogs",
"id":"18",
"param":"Twitter"},
"content":{
"text":"Description of post"},
"language":{
"name":"English",
"code":"en"},
"abstracttext":"More Text and links",
"score":{}
}
}
in order to call the data into my application, I have to turn the JSON into a string using this code:
DECLARE #input string = #"/MSEStream/{*}.json";
REFERENCE ASSEMBLY [Newtonsoft.Json];
REFERENCE ASSEMBLY [Microsoft.Analytics.Samples.Formats];
#allposts =
EXTRACT
jsonString string
FROM #input
USING Extractors.Text(delimiter:'\b', quoting:true);
#extractedrows = SELECT Microsoft.Analytics.Samples.Formats.Json.JsonFunctions.JsonTuple(jsonString) AS er FROM #allposts;
#result =
SELECT er["id"] AS postID,
er["contenttype"] AS contentType,
er["posttype"] AS postType,
er["uri"] AS uri,
er["title"] AS Title,
er["acquisitiondate"] AS acquisitionDate,
er["modificationdate"] AS modificationDate,
er["publicationdate"] AS publicationDate,
er["profile"] AS profile
FROM #extractedrows;
OUTPUT #result
TO "/ProcessedQueries/all_posts.csv"
USING Outputters.Csv();
This output the JSON into a .csv file that is readable and when I download the file all data is displayed properly. My problem is when I need to get the data inside profile. Because the JSON is now a string I can't seem to extract any of that data and put it into a variable to use. Is there any way to do this? or do I need to look into other options for reading the data?
You can use JsonTuple on the profile string to further extract the specific properties you want. An example of U-SQL code to process nested Json is provided in this link - https://github.com/Azure/usql/blob/master/Examples/JsonSample/JsonSample/NestedJsonParsing.usql.
You can use JsonTuple on the profile column to further extract specific nodes
E.g. use JsonTuple to get all the child nodes of the profile node and extract specific values like how you did in your code.
#childnodesofprofile =
SELECT
Microsoft.Analytics.Samples.Formats.Json.JsonFunctions.JsonTuple(profile) AS childnodes_map
FROM #result;
#values =
SELECT
childnodes_map["name"] AS name,
childnodes_map["id"] AS id
FROM #result;
Alternatively, if you are interested in specific values, you can also pass paramters to the JsonTuple function to get the specific nodes you want. The code below gets the locality node from the recursively nested nodes (as described by the "$..value" construct.
#locality =
SELECT Microsoft.Analytics.Samples.Formats.Json.JsonFunctions.JsonTuple(profile, "$..locality").Values AS locality
FROM #result;
Other supported constructs by JsonTuple
JsonTuple(json, "id", "name") // field names
JsonTuple(json, "$.address.zip") // nested fields
JsonTuple(json, "$..address") // recursive children
JsonTuple(json, "$[?(#.id > 1)].id") // path expression
JsonTuple(json) // all children
Hope this helps.

JSON - look up values in array

With the following json
{
"Count":0,
"Message":{
"AppId":0
},
"Data":"[{\"application_name\": \"Grand Central\",\"feature_name\": \"1 Click Fix\",\"access_type_id\": 2,\"member_name\": \"GC_Remote_Support_Security\"},{\"application_name\": \"Grand Central\",\"feature_name\": \"Account Details\",\"access_type_id\": 2,\"member_name\": \"GC_Remote_Support_Security\"},{\"application_name\": \"Grand Central\",\"feature_name\": \"Account Summary\",\"access_type_id\": 2,\"member_name\": \"GC_Remote_Support_Security\"}]"
}
how do I go through the Data array, in the most succinct coding manner possible, to see if any feature_name matches a given string?
Since your JSON contains nested, quoted JSON, you will need nested deserializations using LINQ to JSON to parse your Data array. Having done so, you can use use SelectTokens to query with a JSONPath query to find nested properties named feature_name, then check their value:
var testString = "Account Summary";
var found = JToken.Parse(JObject.Parse(jsonString)["Data"].ToString()).SelectTokens("..feature_name").Any(t => (string)t == testString);
Debug.Assert(found == true); // No assert.
Update
If you want the all JObject with a "feature_name" property matching a given value, you can do:
var foundItems = JToken.Parse(JObject.Parse(jsonString)["Data"].ToString())
.SelectTokens("..feature_name")
.Where(t => (string)t == testString)
.Select(t => t.Ancestors().OfType<JObject>().First()) // Get the immediate parent JObject of the matching value
.ToList();

SelectNodes and GetElementsByTagName

what are main differences between SelectNodes and GetElementsByTagName.
SelectNodes is a .NET/MSXML-specific method that gets a list of matching nodes for an XPath expression. XPaths can select elements by tag name but can also do lots of other, more complicated selection rules.
getElementByTagName is a DOM Level 1 Core standard method available in many languages (but spelled with a capital G in .NET). It selects elements only by tag name; you can't ask it to select elements with a certain attribute, or elements with tag name a inside other elements with tag name b or anything clever like that. It's older, simpler, and in some environments faster.
SelectNodes takes an XPath expression as a parameter and returns all nodes that match that expression.
GetElementsByTagName takes a tag name as a parameter and returns all tags that have that name.
SelectNodes is therefore more expressive, as you can write any GetElementsByTagName call as a SelectNodes call, but not the other way around. XPath is a very robust way of expressing sets of XML nodes, offering more ways of filtering than just name. XPath, for example, can filter by tag name, attribute names, inner content and various aggregate functions on tag children as well.
SelectNodes() is a Microsoft extension to the Document Object Model (DOM) (msdn).
SelectNodes as mentioned by Welbog and others takes XPath expression. I would like to mention difference with GetElementsByTagName() when deleting xml node is needed.
Answer and code provided user chilberto at msdn forum
The next test illustrates the difference by performing the same function (removing the person nodes) but by using the GetElementByTagName() method to select the nodes. Though the same object type is returned its construction is different. The SelectNodes() is a collection of references back to the xml document. That means we can remove from the document in a foreach without affecting the list of references. This is shown by the count of the nodelist not being affected. The GetElementByTagName() is a collection that directly reflects the nodes in the document. That means as we remove the items in the parent, we actually affect the collection of nodes. This is why the nodelist can not be manipulated in a foreach but had to be changed to a while loop.
.NET SelectNodes()
[TestMethod]
public void TestSelectNodesBehavior()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<root>
<person>
<id>1</id>
<name>j</name>
</person>
<person>
<id>2</id>
<name>j</name>
</person>
<person>
<id>1</id>
<name>j</name>
</person>
<person>
<id>3</id>
<name>j</name>
</person>
<business></business>
</root>");
XmlNodeList nodeList = doc.SelectNodes("/root/person");
Assert.AreEqual(5, doc.FirstChild.ChildNodes.Count, "There should have been a total of 5 nodes: 4 person nodes and 1 business node");
Assert.AreEqual(4, nodeList.Count, "There should have been a total of 4 nodes");
foreach (XmlNode n in nodeList)
n.ParentNode.RemoveChild(n);
Assert.AreEqual(1, doc.FirstChild.ChildNodes.Count, "There should have been only 1 business node left in the document");
Assert.AreEqual(4, nodeList.Count, "There should have been a total of 4 nodes");
}
.NET GetElementsByTagName()
[TestMethod]
public void TestGetElementsByTagNameBehavior()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<root>
<person>
<id>1</id>
<name>j</name>
</person>
<person>
<id>2</id>
<name>j</name>
</person>
<person>
<id>1</id>
<name>j</name>
</person>
<person>
<id>3</id>
<name>j</name>
</person>
<business></business>
</root>");;
XmlNodeList nodeList = doc.GetElementsByTagName("person");
Assert.AreEqual(5, doc.FirstChild.ChildNodes.Count, "There should have been a total of 5 nodes: 4 person nodes and 1 business node");
Assert.AreEqual(4, nodeList.Count, "There should have been a total of 4 nodes");
while (nodeList.Count > 0)
nodeList[0].ParentNode.RemoveChild(nodeList[0]);
Assert.AreEqual(1, doc.FirstChild.ChildNodes.Count, "There should have been only 1 business node left in the document");
Assert.AreEqual(0, nodeList.Count, "All the nodes have been removed");
}
With SelectNodes() we get collection / list of references to xml document nodes. We can manipulate with those references. If we delete node, the change will be visible to xml document, but the collection / list of references is the same (although node which was deleted, it's reference points now to null -> System.NullReferenceException) Although I do not really know how this is implemented. I suppose if we use XmlNodeList nodeList = GetElementsByTagName() and delete node with nodeList[i].ParentNode.RemoveChild(nodeList[i]) is frees/deletes reference in nodeList variable.