Hy,
I have for example this xml:
<books>
<book1 name="Cosmic">
<attribute value="good"/>
</book1>
</books>
How can I display it in a listBox control line by line, that the final result it will be a listbox with 5 rows in this case?
In this moment I am prasing the XML using LINQ to XML like this:
foreach (XElement element in document.DescendantNodes())
{
MyListBox.Items.Add(element.ToString());
}
But the final result puts every xml node in one list-box item (including child-nodes).
Does anyone has any idea how can I put the xml line by line in list-box items?
Thanks.
Jeff
A simple solution would use a recursive function like the following:
public void FillListBox(ListBox listBox, XElement xml)
{
listBox.Items.Add("<" + xml.Name + ">");
foreach (XNode node in xml.Nodes())
{
if (node is XElement)
// sub-tag
FillListBox(listBox, (XElement) node);
else
// piece of text
listBox.Items.Add(node.ToString());
}
listBox.Items.Add("</" + xml.Name + ">");
}
Of course, this one will print only the tag names (e.g. <book1> in your example) and not the attributes (name="Cosmic" etc.). I’m sure you can put those in yourself.
If you want to display your raw XML in a list box, use a text stream to read in your data.
using(StreamReader re = File.OpenText("Somefile.XML"))
{
string input = null;
while ((input = re.ReadLine()) != null)
{
MyListBox.Items.Add(input);
}
}
Jeff, maybe it would be much easier to implement (and to read/maintain) with a simple TextReader.ReadLine()?
I don't know what you are trying to achieve, just a suggestion.
Related
I need to convert text file with some conversion columns-elements to an nested xml file -
for example
txt flat file - Name,Age,Street name,street number,city name
conversion table-
flat file - Name,Age,Street name,street number,city name
conversion table -
Name-FullName
Age-Age
Street Name-AddressDetail-StreetName
Street Number-AddressDetail-StreetNumber
City Name-AddressDetail-CityName
xml file
<?xml version="1.0" encoding="UTF-8"?>
<Envelop>
<FullName>Steve Mate</FullName>
<Age>22</Age>
<AddressDetail>
<StreetName>Rockford</StreetName>
<StreetNumber>111</StreetNumber>
<CityName>Alimena</CityName>
</AddressDetail>
</Envelop>
What is the best way to implement it?
Can it be used with MVC?
Using XElement?
The following code should work for you
var lines = text.Split(new[] {"\r", "\n", "\r\n"}, 0);
var xDoc = new XDocument(new XElement("Envelop"));
foreach (var line in lines)
{
var values = line.Split('-');
var parentNode = xDoc.Root;
foreach (var value in values.Reverse().Skip(1))
{
var name = value.Replace(" ", "");
var node = parentNode.Element(name);
if (node == null)
{
node = new XElement(name);
parentNode.Add(node);
}
parentNode = node;
}
parentNode.Value = values.Last();
}
Split the text based on newlines.
For each line, split it based on -
Take the root the starting node
For each value in the split line, skipping the first item...
Remove spaces (you can't have spaces in a node name).
Get the child node with that name
If it doesn't exist, create it.
Set the current parent as this node.
Finally set the value of this node to the last item in the split values
dotnetfiddle
Output:
<Envelop>
<FullName>FullName</FullName>
<Age>Age</Age>
<AddressDetail>
<StreetName>StreetName</StreetName>
<StreetNumber>StreetNumber</StreetNumber>
<CityName>CityName</CityName>
</AddressDetail>
</Envelop>
I've done similar a number of times.
There is no special trick to this. You will need to parse the input file to get the elements you need, and then you'll need to create an XML file with the data.
I don't know of any existing way to convert your particular text file format.
I have a problem where I need to get the value of a specific node in c#
I have this sample XML-Code and here is my C# code
string xml = #"
<ChapterHeader>
<Text> I need to get the text here</Text>
</ChapterHeader>
";
XmlReader rdr = XmlReader.Create(new System.IO.StringReader(xml));
while (rdr.Read())
{
if (rdr.NodeType == XmlNodeType.Element)
{
Console.WriteLine(rdr.LocalName);
if (rdr.LocalName == "ChapterHeader")
{
Console.WriteLine(rdr.Value);
}
}
}
The desired output is
<Text> I need to get the text here</Text>
including the Text Node. How can i do that? thank you
I also need to loop a huge xml file
and I need to get the value of a specific node
and I need to skip some specific node also.
example I have a node. the program must not read that Node and its childen Node.
How can i do that?
<ChapterHeader>
<Text> I need to get the text here</Text>
</ChapterHeader>
<Blank>
<Not>
</Not>
</Blank>
The desired output is
<Text> I need to get the text here</Text>
Look for ReadInnerXml which reads all the content, including markup, as a string.
Console.WriteLine( rdr.ReadInnerXml());
In the following question, you want to deal with larger Xml. I prefer Linq to Xml when dealing with larger set.
The program must not read that Node and its childen Node
Yes, it is possible. You could do something like this.
XDocument doc = XDocument.Load("filepath");
var nestedElementValues =
doc.Descendants("ChapterHeader") // flattens hierarchy and look for specific name.
.Elements() // Get elements for found element
.Select(x=>(string)x.Value); // Read the value.
Check this Example
System.Xml.Linq is a newer library designed to get rid of undesired reader style.
var document = XDocument.Parse(xml);
var texts = document.Descendants("Text");
foreach (var text in texts)
{
Console.WriteLine(text);
}
You can use the same parsing style you're using (rdr.LocalName = "Text") and then use rdr.ReadOuterXml()
Here's some fantastic example XML:
<root>
<section>Here is some text<mightbe>a tag</mightbe>might <not attribute="be" />. Things are just<label>a mess</label>but I have to parse it because that's what needs to be done and I can't <font stupid="true">control</font> the source. <p>Why are there p tags here?</p>Who knows, but there may or may not be spaces around them so that's awesome. The point here is, there's node soup inside the section node and no definition for the document.</section>
</root>
I'd like to just grab the text from the section node and all sub nodes as strings. BUT, note that there may or may not be spaces around the sub-nodes, so I want to pad the sub notes and append a space.
Here's a more precise example of what input might look like, and what I'd like output to be:
<root>
<sample>A good story is the<book>Hitchhikers Guide to the Galaxy</book>. It was published<date>a long time ago</date>. I usually read at<time>9pm</time>.</sample>
</root>
I'd like the output to be:
A good story is the Hitchhikers Guide to the Galaxy. It was published a long time ago. I usually read at 9pm.
Note that the child nodes don't have spaces around them, so I need to pad them otherwise the words run together.
I was attempting to use this sample code:
XDocument doc = XDocument.Parse(xml);
foreach(var node in doc.Root.Elements("section"))
{
output += String.Join(" ", node.Nodes().Select(x => x.ToString()).ToArray()) + " ";
}
But the output includes the child tags, and is not going to work out.
Any suggestions here?
TL;DR: Was given node soup xml and want to stringify it with padding around child nodes.
Incase you have nested tags to an unknown level (e.g <date>a <i>long</i> time ago</date>), you might also want to recurse so that the formatting is applied consistently throughout. For example..
private static string Parse(XElement root)
{
return root
.Nodes()
.Select(a => a.NodeType == XmlNodeType.Text ? ((XText)a).Value : Parse((XElement)a))
.Aggregate((a, b) => String.Concat(a.Trim(), b.StartsWith(".") ? String.Empty : " ", b.Trim()));
}
You could try using xpath to extract what you need
var docNav = new XPathDocument(xml);
// Create a navigator to query with XPath.
var nav = docNav.CreateNavigator();
// Find the text of every element under the root node
var expression = "/root//*/text()";
// Execute the XPath expression
var resultString = nav.evaluate(expression);
// Do some stuff with resultString
....
References:
Querying XML, XPath syntax
Here is a possible solution following your initial code:
private string extractSectionContents(XElement section)
{
string output = "";
foreach(var node in section.Nodes())
{
if(node.NodeType == System.Xml.XmlNodeType.Text)
{
output += string.Format("{0}", node);
}
else if(node.NodeType == System.Xml.XmlNodeType.Element)
{
output += string.Format(" {0} ", ((XElement)node).Value);
}
}
return output;
}
A problem with your logic is that periods will be preceded by a space when placed right after an element.
You are looking at "mixed content" nodes. There is nothing particularly special about them - just get all child nodes (text nodes are nodes too) and join they values with space.
Something like
var result = String.Join("",
root.Nodes().Select(x => x is XText ? ((XText)x).Value : ((XElement)x).Value));
I have a following XML file:
<data>
<set1>
<entry>Entry #1</entry>
<entry>Entry #2</entry>
</set1>
</data>
and I'm trying to read all the descendants of the SET element. Im not sure If I'm confusing terms like element and descendant, so Im just gonna stop using them for now :)
Here is the C# code that I use:
List<String> list = new List<String>();
XDocument xml = XDocument.Load("file.xml");
var desc = xml.Descendants("set1");
foreach (var entry in desc) {
list.add(entry.Value);
Console.Write("element: " + entry.Value);
}
But instead of two lines of console output "Entry #1" and "Entry #2" I only get one "Entry #1Entry #2". Thanks for your help!
Something Like :
var desc = xd.Descendants("set1").Elements("entry");
Change to
var desc = xml.Descendants("set1").Descendants();
And also
Console.WriteLine
I just tried it and it works.
The error is that Descendants("set1") does not give you the descendants of set1. It gives you the xml's root descendants that are called set1.
As for the Console.WriteLine - it adds a newline at the end of what it writes.
I have a big (~40mb) collection of XML data, split in many files which are not well formed, so i merge them, add a root node and load all the xml in a XmlDocument. Its basically a list of 3 different types which can be nested in a few different ways. This example should show most of the cases:
<Root>
<A>
<A>
<A></A>
<A></A>
</A>
</A>
<A />
<B>
<A>
<A>
<A></A>
<A></A>
</A>
</A>
</B>
<C />
</Root>
Im separating all A, B and C nodes by using XPath expressions on a XmlDocument (//A, //B, //C), convert the resulting nodesets to a datatable and show a list of all nodes of each nodetype separately in a Datagridview. This works fine.
But now Im facing an even bigger file and as soon as i load it, it shows me only 4 rows. Then i added a breakpoint at the line where the actual XmlDocument.SelectNodes happens and checked the resulting NodeSet. It shows me about 25,000 entries. After continuing the program loaded and whoops, all my 25k rows were shown. I tried it again and i can reproduce it. If i step over XmlDocument.SelectNodes by hand, it works. If i dont break there, it does not. Im not spawning a single thread in my application.
How can i debug this any further? What to look for? I have experienced such behaviour with multithreaded libraries such as jsch (ssh) but im dont see why this should happen in my case.
Thank you very much!
// class XmlToDataTable:
private DataTable CreateTable(NamedXPath logType,
List<XmlColumn> columns,
ITableCreator tableCreator)
{
// I have to break here -->
XmlNodeList xmlNodeList = logFile.GetEntries(logType);
// <-- I have to break here
DataTable dataTable = tableCreator.CreateTableLayout(columns);
foreach (XmlNode xmlNode in xmlNodeList)
{
DataRow row = dataTable.NewRow();
tableCreator.PopulateRow(xmlNode, row, columns);
dataTable.Rows.Add(row);
}
return dataTable;
}
// class Logfile:
public XmlNodeList GetEntries(NamedXPath e)
{
return (_xmlDocument != null && _xmlDocument.HasChildNodes)
? _xmlDocument.SelectNodes(e.XPath)
: new XmlNullObjectNodeList();
}
// _xmlDocument gets loaded here after reading all xml fragments into a string
// (ugly, i know. the // ugly! comment reminds me about that ;))
private void CreateXmlDoc()
{
_xmlDocument = new XmlDocument();
_xmlDocument.LoadXml(OPEN_ROOT_ELEMENT + _xmlString +
CLOSE_ROOT_ELEMENT);
if (DataChanged != null)
DataChanged(this, new EventArgs());
}
// class NamedXPath:
public abstract class NamedXPath
{
private readonly String _name;
private readonly String _xPath;
protected NamedXPath(string name, string xPath)
{
_name = name;
_xPath = xPath;
}
public string Name
{
get { return _name; }
}
public string XPath
{
get { return _xPath; }
}
}
Instead of using XPath directly in the code first, I would use a tool such as sketchPath to get my XPath right. You can either load your original XML or use subset of original XML.
Play with XPath and your XML to see if the expected nodes are getting selected before using xpath in your code.
Okay, solved it. tableCreator is part of my strategy pattern, which influences the way the table is built. In a certain implementation I do something like this:
XmlNode xn = xmlDocument.SelectSingleNode(fancyXPath);
// if a node has ancestors, then its a linked list:
// <a><a><a></a></a></a>
if(xn.SelectSingleNode("a") != null)
xn.SelectSingleNode("a").InnerText = "<IDs of linked list items CSV like here>";
Which means im replacing parts of a xml linked list with some text and lose the nested items there.
Wouldn't be a problem to find this bug if this change wouldn't affect the original XmlDocument. Even then, debugging it should not be too hard. What makes my program behaving differently depending whether I break or not seems to be the following:
Return Value:
The first XmlNode that
matches the XPath query or null if no
matching node is found. The XmlNode
should not be expected to be connected
"live" to the XML document. That is,
changes that appear in the XML
document may not appear in the
XmlNode, and vice versa. (API
Description of XmlNode.SelectNodes())
If I break there, the changes are written back to the original XmlDocument, if I don't break, its not written back. Can't really explain that to myself, but without the change in the XmlNode everything works.
edit:
Now im quite sure: I had XmlNodeList.Count in my watches. This means, everytime i debugged, VS called the property Count, which not only returns a number but calls ReadUntil(int), which refreshes the internal list:
internal int ReadUntil(int index)
{
int count = this.list.Count;
while (!this.done && (count <= index))
{
if (this.nodeIterator.MoveNext())
{
XmlNode item = this.GetNode(this.nodeIterator.Current);
if (item != null)
{
this.list.Add(item);
count++;
}
}
else
{
this.done = true;
return count;
}
}
return count;
}
This may have caused that weird behavior.