I have a Java generated (List Collection) XML format of:
<java.util.Collections>
<org.yccheok.jstock.engine.Stock>
<code>
<code> RBS.L</code>
</code>
<symbol>
<symbol> ROYAL BK SCOTL GR</symbol>
</symbol>
<name> ROYAL BK SCOTL GR</name>
<board> London</board>
<industry> Unknown</industry>
<prevPrice> 23.74</prevPrice>
<openPrice> 23.41</openPrice>
<lastPrice> 24.4</lastPrice>
<highPrice> 24.855</highPrice>
<lowPrice> 23.0</lowPrice>
<volume> 51353968</volume>
<changePrice> 0.66</changePrice>
<changePricePercentage> 2.78</changePricePercentage>
<lastVolume> 795</lastVolume>
<buyPrice> 24.39</buyPrice>
<buyQuantity> 51203</buyQuantity>
<sellPrice> 24.4</sellPrice>
<sellQuantity> 370763</sellQuantity>
<secondBuyPrice> 0.0</secondBuyPrice>
<secondBuyQuantity> 0</secondBuyQuantity>
<secondSellPrice> 0.0</secondSellPrice>
<secondSellQuantity> 0</secondSellQuantity>
<thirdBuyPrice> 0.0</thirdBuyPrice>
<thirdBuyQuantity> 0</thirdBuyQuantity>
<thirdSellPrice> 0.0</thirdSellPrice>
<thirdSellQuantity> 0</thirdSellQuantity>
<calendar>
<time> 1319038099446</time>
<timezone> America/New_York</timezone>
</calendar>
</org.yccheok.jstock.engine.Stock>
</java.util.Collections>
I am trying to extract the tag values of the code inner tag and changePricePercentage in C#. I am also trying to pre-populate a DataTable with these values. How do I handle the inner tag of code as well? Even though I am no expert, here is my C# source code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data;
namespace XMLParser
{
class Program
{
static void Main(string[] args)
{
DataTable table = new DataTable();
table.Columns.Add("code", typeof(string)); ;
table.Columns.Add("changePricePercentage", typeof(double));
// Create a new XmlDocument
XmlDocument doc = new XmlDocument();
// Load data
doc.Load(#"C:\...\realtimestock.xml");
// Set up namespace manager for XPath
// Get forecast with XPath
//XmlNodeList nodes = doc.SelectNodes("org.yccheok.jstock.engine.Stock", ns);
XmlNodeList nodes = doc.SelectNodes("org.yccheok.jstock.engine.Stock");
// You can also get elements based on their tag name and namespace,
// though this isn't recommended
//XmlNodeList nodes = doc.GetElementsByTagName("org.yccheok.jstock.engine.Stock");
// "http://xml.weather.yahoo.com/ns/rss/1.0");
foreach (XmlNode node in nodes)
{
// Console.WriteLine("{0}: {1}, {2}F - {3}F",
// node.Attributes["code"].InnerText,
// node.Attributes["changePricePercentage"].InnerText);
Console.WriteLine("1: {0} 2: {1}", node.Attributes["code"].InnerText,
node.Attributes["changePricePercentage"].InnerText);
table.Rows.Add(node.Attributes["code"].InnerText, node.Attributes["changePricePercentage"].InnerText);
Console.ReadKey();
}
}
}
}
How would I get my code to accomplish this task?
P.S. This Stackoverflow editor would not accept my XML code properly so I had to edit with the actual symbol names. Sorry
Thanks
Try this:
XmlNodeList nodes = doc.SelectNodes("//org.yccheok.jstock.engine.Stock");
foreach (XmlElement element in nodes)
{
Console.WriteLine("1: {0} 2: {1}",
element.SelectSingleNode("code").InnerText,
element.SelectSingleNode("changePricePercentage").InnerText);
}
Console.ReadKey();
Your code and changePricePercentage nodes are elements, not attributes, that was your mistake.
Related
I am trying to update the content of multiple nodes that match a specific xpath. For example, in the below example, I want to update any attribute1 node that currently is '2' with 'Hello'. What is the best way to do this?
(DotNetFiddle Here)
using System;
using System.IO;
using System.Xml;
using System.Linq;
public class Program
{
public static void Main()
{
XmlDocument job = new XmlDocument();
job.LoadXml(#"<parent>" +
"<report>" +
"<main>" +
"<attribute1>2</attribute1>"+
"<attribute1>2</attribute1>"+
"<attribute1>3</attribute1>"+
"</main>"+
"</report>" +
"</parent>");
string newAttribute1Value = "Hello";
//How do I update both attribute1's where value=2?
}
This may could help you :
using System;
using System.IO;
using System.Xml;
using System.Linq;
public class Program
{
public static void Main()
{
XmlDocument job = new XmlDocument();
job.LoadXml(#"<parent>" +
"<report>" +
"<main>" +
"<attribute1>2</attribute1>"+
"<attribute1>2</attribute1>"+
"<attribute1>3</attribute1>"+
"</main>"+
"</report>" +
"</parent>");
string newAttribute1Value = "Hello";
//How do I update both attribute1's where value=2?
// getting the list of nodes with XPath query :
XmlNodeList nodes = job.SelectNodes("//attribute1[text()=2]");
foreach (XmlNode child in nodes)
{
// replacing the old value with the new value
child.InnerText = newAttribute1Value ;
}
}
I have tried to setup a Dialogue tree within unity using XML (I have not used XML much before so am unsure if the way i am going is correct at all)
So I am trying to get the first text element from this dialogue tree but when i call the XML file and say where it is i am getting the everything stored in that branch.
Am i using the correct .XML to be able to do this also as i seen people say use .XML.LINQ or .XML.Serialization not just .XML is this correct for my case ??
Code:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Xml;
using UnityEngine.UI;
using System.Collections.Generic;
public class DialogTree
{
public string text;
public List<string> dialogText;
public List<DialogTree> nodes;
public void parseXML(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new StringReader(xmlData));
XmlNode node = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
text = node.InnerXml;
XmlNodeList myNodeList = xmlDoc.SelectNodes("dialoguebranch/dialoguebranch");
foreach (XmlNode node1 in myNodeList)
{
if (node1.InnerXml.Length > 0)
{
DialogTree dialogtreenode = new DialogTree();
dialogtreenode.parseXML(node1.InnerXml);
nodes.Add(dialogtreenode);
}
}
}
}
And here is a picture of the XML.
So i am trying to grab the first element of text then late on there response it will go to branch 1 or 2
<?xml version='1.0'?>
<dialoguetree>
<dialoguebranch>
<text>Testing if the test prints</text>
<dialoguebranch>
<text>Branch 1</text>
<dialoguebranch>
<text>Branch 1a</text>
</dialoguebranch>
<dialoguebranch>
<text>Branch 1b</text>
</dialoguebranch>
</dialoguebranch>
<dialoguebranch>
<text>Branch 2</text>
</dialoguebranch>
</dialoguebranch>
</dialoguetree>
You're getting everything in that branch because XmlNode.InnerXML returns everything in that node. See the documentation for more information on that.
You should use the branch as the base for only looking at its children, instead of starting at xmlDoc every time. Also, you need an entry point to get inside of the first dialoguetree element and then ignore that. Finally, I would only create one XmlDocument and just pass around nodes in your recursion.
Altogether, this might look like this:
public class DialogTree
{
public string text;
public List<DialogTree> nodes = new List<DialogTree>();
public static DialogTree ParseXMLStart(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new Stringreader(xmlData));
XmlNode rootNode = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(rootNode);
return dialogTree;
}
public void ParseXML(XmlNode parentNode)
{
XmlNode textNode = parentNode.SelectSingleNode("text");
text = textNode.InnerText;
XmlNodeList myNodeList = parentNode.SelectNodes("dialoguebranch");
foreach (XmlNode curNode in myNodeList)
{
if (curNode.InnerXml.Length > 0)
{
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(curNode);
nodes.Add(dialogTree);
}
}
}
}
And you could use it like so:
string xmlStringFromFile;
DialogTree dialogue = DialogTree.ParseXMLStart(xmlStringFromFile);
All of this code is untested but I hope the general idea is clear. Let me know if you find any errors in the comments below and I will try to fix them.
How to parse all the XML files under a given directory as an input to the application and write its output to a text file.
Note: The XML is not always the same the nodes in the XML can vary and have any number of Child-nodes.
Any help or guidance would be really helpful on this regard :)
XML File Sample
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>
<CNT>USA</CNT>
<CODE>3456</CODE>
</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
</CATALOG>
C# Code
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace XMLTagParser
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
string loc = locNode.Name;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
catch {
Console.WriteLine("Error :-(");
}
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
}
}
Preferred Output:
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY/CNT
CATALOG/CD/COUNTRY/CODE
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
CATALOG/CD/TITLE
CATALOG/CD/ARTIST
CATALOG/CD/COUNTRY
CATALOG/CD/COMPANY
CATALOG/CD/PRICE
CATALOG/CD/YEAR
This is a recursive problem, and what you are looking for is called 'tree traversal'. What this means is that for each child node, you want to look into it's children, then into that node's children (if it has any) and so on, recording the 'path' as you go along, but only printing out the names of the 'leaf' nodes.
You will need a function like this to 'traverse' the tree:
static void traverse(XmlNodeList nodes, string parentPath)
{
foreach (XmlNode node in nodes)
{
string thisPath = parentPath;
if (node.NodeType != XmlNodeType.Text)
{
//Prevent adding "#text" at the end of every chain
thisPath += "/" + node.Name;
}
if (!node.HasChildNodes)
{
//Only print out this path if it is at the end of a chain
Console.WriteLine(thisPath);
}
//Look into the child nodes using this function recursively
traverse(node.ChildNodes, thisPath);
}
}
And then here is how I would add it into your program (within your foreach sitemap loop):
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// start traversing from the children of the root node
var rootNode = xDoc.FirstChild;
traverse(rootNode.ChildNodes, rootNode.Name);
}
catch
{
Console.WriteLine("Error :-(");
}
I made use of this other helpful answer: Traverse a XML using Recursive function
Hope this helps! :)
The XML file is like this,There are about 20 Nodes(modules) like this.
<list>
<module code="ECSE502">
<code>ECSE502</code>
<name>Algorithms and Data structures</name>
<semester>1</semester>
<prerequisites>none</prerequisites>
<lslot>0</lslot>
<tslot>1</tslot>
<description>all about algorythms and data structers with totorials and inclass tests</description>
</module>
</list>
I did the following code. But when I debugged it it even didn't went inside to foreach function.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
namespace ModuleEnrolmentCW
{
class XMLRead
{
public string[] writeToXML(string s)
{
string text = s;
string[] arr = new string[6];
XmlDocument xml = new XmlDocument();
xml.Load("modules.xml");
XmlNodeList xnList = xml.SelectNodes("list/module[#code='" + text + "']");
foreach (XmlNode xn in xnList)
{
arr[0] = xn.SelectSingleNode("code").InnerText;
arr[1] = xn.SelectSingleNode("name").InnerText;
arr[2] = xn.SelectSingleNode("semester").InnerText;
arr[3] = xn.SelectSingleNode("prerequisites").InnerText;
arr[4] = xn.SelectSingleNode("lslot").InnerText;
arr[5] = xn.SelectSingleNode("tslot").InnerText;
}
return arr;
}
}
}
Please tell me where is the wrong??
Here is the rest of the code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ModuleEnrolmentCW
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string selected;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
XMLRead x = new XMLRead();
selected = (string)listBox1.SelectedItem;
string[] arr2 = x.writeToXML(selected);
label11.Text = arr2[0];
}
}
}
Make sure you are specifying correct path for your xml file.
It is working for me.
This line:
XmlNodeList xnList = xml.SelectNodes("list/module[#code='" + text + "']");
should read:
XmlNodeList xnList = xml.SelectNodes("list/module"); //Does not answer full scope of the question
Edit following a reread of the question:
The OP's code works fine in my tests. Either the file path is not correct, or the the string s passed into text matches the case of the Code value by which you are reading the nodes.
The SelectNodes XPath as you have it is case sensitive.
You appear to be working with XPath V1.0 which doesn't appear to support out of the box case insensitivity if that's a issue. See this link for a way to perform case insensitive XPath searches: http://blogs.msdn.com/b/shjin/archive/2005/07/22/442025.aspx
See also this link: case-insensitive matching in xpath?
Your code is correct, if the input is really the one you shown, and s point to an actual present code. Since you are pointing the file by a relative path, ensure you are loading the file you really expect.
Found the error. I was passing a wrong value to writeToXML method. Insted of passing code, I have passed name
I have one xml file whic I need to edit, It looks like:
<?xml version="1.0" encoding="UTF-8"?>
<PaketniUvozObrazaca xmlns="urn:PaketniUvozObrazaca_V1_0.xsd">
<PodaciOPoslodavcu>
<JIBPoslodavca>XXXXXXXXXX</JIBPoslodavca>
<NazivPoslodavca>Comapyn</NazivPoslodavca>
<BrojZahtjeva>1307</BrojZahtjeva>
<DatumPodnosenja>2013-03-19</DatumPodnosenja>
</PodaciOPoslodavcu>
<Obrazac1022>
<Dio1PodaciOPoslodavcuIPoreznomObvezniku>
<JIBJMBPoslodavca>XXXXXXXXXX</JIBJMBPoslodavca>
<Naziv>Compyny</Naziv>
<AdresaSjedista>Adress </AdresaSjedista>
<JMBZaposlenika>XXXXXXX</JMBZaposlenika>
<ImeIPrezime>ad</ImeIPrezime>
<AdresaPrebivalista>City</AdresaPrebivalista>
<PoreznaGodina>2012</PoreznaGodina>
</Dio1PodaciOPoslodavcuIPoreznomObvezniku>
</Obrazac1022>
</PaketniUvozObrazaca>
If i want to query (select nodes) from this file how I should reffernce namespace ?
static void Main(string[] args)
{
XmlDocument gip = new XmlDocument();
gip.Load("C:\\vs2013tests\\adoGipko\\gip-2012.xml");
XmlNamespaceManager nspm = new XmlNamespaceManager(gip.NameTable);
nspm.AddNamespace("urn", "PaketniUvozObrazaca_V1_0");
foreach (XmlNode uposlnik in gip.SelectNodes("//PaketniUvozObrazaca_V1_0/Obrazac1022", nspm))
{
Console.WriteLine(uposlnik.SelectSingleNode("Dio1PodaciOPoslodavcuIPoreznomObvezniku/ImeIPrezime",nspm).InnerText);
}
Console.ReadKey();
}
You need to give the namespace a name and specify the uri exactly as in the XML. Which name you use is not important:
nspm.AddNamespace("d", "urn:PaketniUvozObrazaca_V1_0.xsd");
You now have to use that name for all the tags:
foreach (XmlNode uposlnik in gip.SelectNodes("//d:PaketniUvozObrazaca/"+
"d:Obrazac1022", nspm))
{
Console.WriteLine(
uposlnik.SelectSingleNode("d:Dio1PodaciOPoslodavcuIPoreznomObvezniku/"+
"d:ImeIPrezime", nspm).InnerText);
}
Furthermore, I fixed the root tag in the query. You used PaketniUvozObrazaca_V1_0 which is part of the namespace. You need to use PaketniUvozObrazaca, which is the root tag.