Hello I have created an xml file with motivational quotations, and I want to read those quotations into an array.
Here is what my xml file looks like:
<?xml version="1.0" encoding="utf-8" ?>
<MotivationalQuotes>
<quotation>
<quote>Life is about making an impact, not making an income</quote>
<author>Kevin Kruse</author>
</quotation>
<quotation>
<quote>Whatever the mind of man can conceive and believe, it can achieve</quote>
<author>Napoleon Hill</author>
</quotation>
</MotivationalQuotes>
I am trying to store each individual quotation (without the author) into an array, so far I have the below code working - which creates a message box and iterates through the xml file displaying the text from each quotation.
1) How can I modify this code to create a string array, where each item in the array is a quotation (i.e. each item in the array is the contents that is currently being displayed to the messagebox in my foreach loop?
2) how do I return a random item from the array, once it is created?
3) As an extension to my question... my xml file only has motivational quotes in at the moment, but it will have more inspirational, funny etc... how can I specify to only include quotations into the array if they are inside the MotivationalQuotes tag.
Thanks for the help!
public void motivate()
{
XmlDocument doc = new XmlDocument();
doc.Load("quotations.xml");
XmlNode Node = doc.DocumentElement;
foreach (XmlNode Node1 in Node.ChildNodes)
{
MessageBox.Show(Node1.FirstChild.InnerText);
}
}
You should use XDocument and LINQ.
To get all quotes
using System.Xml.Linq;
var quotes = XDocument
.Load("quotations.xml")
.Descendants("quote")
.Select(q => q.Value)
.ToArray();
Related
Hello i am trying to get simple xml file from server and to read the data so i can convert it to list. I was try few lib and code with no success so far. I am getting the xml content in one line without any tags <> and the count is always 0 zero. The XML string. I need to get the data that inside camp tag
<campaigns>
<mainPage>http://example.com</mainPage>
<orderPage>https://www.example.co.il/adver/</orderPage>
<totalCount>3</totalCount>
<onLineCount>2</onLineCount>
<campaignList>
<camp id="557">
<name>test1</name>
<status>on</status>
<rating>5</rating>
<url>http://example.com/557</url>
</camp>
<camp id="559">
<name>test1</name>
<status>on</status>
<rating>5</rating>
<url>http://example.com/559</url>
</camp>
<camp id="660">
<name>test1</name>
<status>off</status>
<rating>5</rating>
<url>http://example.com/660</url>
</camp>
</campaignList>
And the c# code i am trying so far
XElement xelement = XElement.Load("http://example.com/test.xml");
var name = from nm in xelement.Elements("camp")
where (string)nm.Element("status") == "on"
select nm;
Response.Write(name.Count());
foreach (XElement xEle in name)
{
Response.Write(xEle);
}
XElement.Elements() means search in children tags. I think what you need is Descendants() or xelement.Element("campaigns").Element("campaignList").Elements("camp")
I'm parsing an XML file, to compare it to another XML file. XML Diff works nicely, but we have found there are a lot of junk tags that exist in one file, not in the other, that have no bearing on our results, but clutter up the report. I have loaded the XML file into memory to do some other things to it, and I'm wondering if there is an easy way at the same time to go through that file, and remove all tags that start with, as an example color=. The value of color is all over the map, so not easy to grab them all remove them.
Doesn't seem to be any way in XML Diff to specify, "ignore these tags".
I could roll through the file, find each instance, find the end of it, delete it out, but I'm hoping there will be something simpler. If not, oh well.
Edit: Here's a piece of the XML:
<numericValue color="-103" hidden="no" image="stuff.jpg" key="More stuff." needsQuestionFormatting="false" system="yes" systemEquivKey="Stuff." systemImage="yes">
<numDef increment="1" maximum="180" minimum="30">
<unit deprecated="no" key="BPM" system="yes" />
</numDef>
</numericValue>
If you are using Linq to XML, you can load your XML into an XDocument via:
var doc = XDocument.Parse(xml); // Load the XML from a string
Or
var doc = XDocument.Load(fileName); // Load the XML from a file.
Then search for all elements with matching names and use System.Xml.Linq.Extensions.Remove() to remove them all at once:
string prefix = "L"; // Or whatever.
// Use doc.Root.Descendants() instead of doc.Descendants() to avoid accidentally removing the root element.
var elements = doc.Root.Descendants().Where(e => e.Name.LocalName.StartsWith(prefix, StringComparison.Ordinal));
elements.Remove();
Update
In your XML, the color="-103" substring is an attribute of an element, rather than an element itself. To remove all such attributes, use the following method:
public static void RemovedNamedAttributes(XElement root, string attributeLocalNamePrefix)
{
if (root == null)
throw new ArgumentNullException();
foreach (var node in root.DescendantsAndSelf())
node.Attributes().Where(a => a.Name.LocalName == attributeLocalNamePrefix).Remove();
}
Then call it like:
var doc = XDocument.Parse(xml); // Load the XML
RemovedNamedAttributes(doc.Root, "color");
I need to take an XML file and create multiple output xml files from the repeating nodes of the input file. The source file "AnimalBatch.xml" looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Animals>
<Animal id="1001">
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>
<Animal id="1002">
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>
<Animal id="1003">
<Quantity>Three</Quantity>
<Color>Blind</Color>
<Name>Mice</Name>
</Animal>
</Animals>
The program needs to split the repeating "Animal" and produce 3 files named: Animal_1001.xml, Animal_1002.xml, and Animal_1003.xml
Each output file should contain just their respective element (which will be the root). The id attribute from AnimalsBatch.xml will supply the sequence number for the Animal_xxxx.xml filenames. The id attribute does not need to be in the output files.
Animal_1001.xml:
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>
Animal_1002.xml
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>
Animal_1003.xml>
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Three</Quantity>
<Adjective>Blind</Adjective>
<Name>Mice</Name>
</Animal>
I want to do this with XmlDocument, since it needs to be able to run on .Net 2.0.
My program looks like this:
static void Main(string[] args)
{
string strFileName;
string strSeq;
XmlDocument doc = new XmlDocument();
doc.Load("D:\\Rick\\Computer\\XML\\AnimalBatch.xml");
XmlNodeList nl = doc.DocumentElement.SelectNodes("Animal");
foreach (XmlNode n in nl)
{
strSeq = n.Attributes["id"].Value;
XmlDocument outdoc = new XmlDocument();
XmlNode rootnode = outdoc.CreateNode("element", "Animal", "");
outdoc.AppendChild(rootnode); // Put the wrapper element into outdoc
outdoc.ImportNode(n, true); // place the node n into outdoc
outdoc.AppendChild(n); // This statement errors:
// "The node to be inserted is from a different document context."
strFileName = "Animal_" + strSeq + ".xml";
outdoc.Save(Console.Out);
Console.WriteLine();
}
Console.WriteLine("END OF PROGRAM: Press <ENTER>");
Console.ReadLine();
}
I think I have 2 problems.
A) After doing the ImportNode on node n into outdoc, I call outdoc.AppendChild(n) which complains: "The node to be inserted is from a different document context." I do not know if this is a scope issue referencing node n within the ForEach loop - or if I am somehow not using ImportNode() or AppendChild properly. 2nd argument on ImportNode() is set to true, because I want the child elements of Animal (3 fields arbitrarily named Quantity, Adjective, and Name) to end up in the destination file.
B) Second problem is getting the Animal element into outdoc. I'm getting '' but I need ' ' so I can place node n inside it. I think my problem is how I am doing: outdoc.AppendChild(rootnode);
To show the xml, I'm doing: outdoc.Save(Console.Out); I do have the code to save() to an output file - which does work, as long as I can get outdoc assembled properly.
There is a similar question at: Split XML in Multiple XML files, but I don't understand the solution code yet. I think I'm pretty close on this approach, and will appreciate any help you can provide.
I'm going to be doing this same task using XmlReader, since I'm going to need to be able to handle large input files, and I understand that XmlDocument reads the whole thing in and can cause memory issues.
That's a simple method that seems what you are looking for
public void test_xml_split()
{
XmlDocument doc = new XmlDocument();
doc.Load("C:\\animals.xml");
XmlDocument newXmlDoc = null;
foreach (XmlNode animalNode in doc.SelectNodes("//Animals/Animal"))
{
newXmlDoc = new XmlDocument();
var targetNode = newXmlDoc.ImportNode(animalNode, true);
newXmlDoc.AppendChild(targetNode);
newXmlDoc.Save(Console.Out);
Console.WriteLine();
}
}
This approach seems to work without using the "var targetnode" statement. It creates an XmlNode object called targetNode from outdoc's "Animal" element in the ForEach loop. I think the main things that were problems in my original code were: A) I was getting nodelist nl incorrectly. And B) I couldn't "Import" node n, I think because it was associated specifically with doc. It had to be created as its own Node.
The problem with the prior proposed solution was the use of the "var" keyword. My program has to assume 2.0 and that came in with v3.0. I like Rogers solution, in that it is concise. For me - I wanted to do each thing as a separate statement.
static void SplitXMLDocument()
{
string strFileName;
string strSeq;
XmlDocument doc = new XmlDocument(); // The input file
doc.Load("D:\\Rick\\Computer\\XML\\AnimalBatch.xml");
XmlNodeList nl = doc.DocumentElement.SelectNodes("//Animals/Animal");
foreach (XmlNode n in nl)
{
strSeq = n.Attributes["id"].Value; // Animal nodes have an id attribute
XmlDocument outdoc = new XmlDocument(); // Create the outdoc xml document
XmlNode targetNode = outdoc.CreateElement("Animal"); // Create a separate node to hold the Animal element
targetNode = outdoc.ImportNode(n, true); // Bring over that Animal
targetNode.Attributes.RemoveAll(); // Remove the id attribute in <Animal id="1001">
outdoc.ImportNode(targetNode, true); // place the node n into outdoc
outdoc.AppendChild(targetNode); // AppendChild to make it stick
strFileName = "Animal_" + strSeq + ".xml";
outdoc.Save(Console.Out); Console.WriteLine();
outdoc.Save("D:\\Rick\\Computer\\XML\\" + strFileName);
Console.WriteLine();
}
}
I have an application that is on .net 2.0 and I am having some difficult with it as I am more use to linq.
The xml file look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<updates>
<files>
<file url="files/filename.ext" checksum="06B9EEA618EEFF53D0E9B97C33C4D3DE3492E086" folder="bin" system="0" size="40448" />
<file url="files/filename.ext" checksum="CA8078D1FDCBD589D3769D293014154B8854D6A9" folder="" system="0" size="216" />
<file url="files/filename.ext" checksum="CA8078D1FDCBD589D3769D293014154B8854D6A9" folder="" system="0" size="216" />
</files>
</updates>
The file is downloaded and readed on the fly:
XmlDocument readXML = new XmlDocument();
readXML.LoadXml(xmlData);
Initially i was thinking it would go with something like this:
XmlElement root = doc.DocumentElement;
XmlNodeList nodes = root.SelectNodes("//files");
foreach (XmlNode node in nodes)
{
... im reading it ...
}
But before reading them I need to know how many they are to use on my progress bar and I am also clueless on how to grab the attribute of the file element in this case.
How could I count how many "file"
ELEMENTS I have (count them before entering the foreach ofc) and read their
attributes ?
I need the count because it will be used to update the progress bar.
Overall it is not reading my xml very well.
before reading them I need to know how many they are to use on my progress bar
Use the XmlNodeList.Count property. Code example below.
Overall it is not reading my xml very well
Here's some tips on reading Xml with the older Xml library.
First, XPath is your friend. It lets you query elements pretty quickly, in a way that is (very) vaguely similar to Linq. In this case, you should change your XPath to get the list of child "file" elements, rather than the parent "files" element.
XmlNodeList nodes = root.SelectNodes("//files");
Becomes
XmlNodeList files = root.SelectNodes("//file");
The //ElementName searches recursively for all elements with that name. XPath is pretty cool, and you should read up on a bit. Here are some links:
http://msdn.microsoft.com/en-us/library/hcebdtae.aspx
http://msdn.microsoft.com/en-us/library/d271ytdx.aspx
Once you have those elements, you can use the XmlElement.Attributes property, coupled with the XmlAttribute.Value property (file.Attributes["url"].Value).
Or you can use the GetAttribute method.
Click this link to the documentation on XmlElement for more info. Remember to switch the .Net Framework version to 2.0 on that page.
XmlElement root = doc.DocumentElement;
XmlNodeList files = root.SelectNodes("//file"); // file element, not files element
int numberOfFiles = files.Count;
// Todo: Update progress bar here
foreach (XmlElement file in files) // These are elements, so this cast is safe-ish
{
string url = file.GetAttribute("url");
string folder = file.GetAttribute("folder");
// If not an integer, will throw. Could use int.TryParse instead
int system = int.Parse(file.GetAttribute("system"));
int size = int.Parse(file.GetAttribute("size"));
// convert this to a byte array later
string checksum = file.GetAttribute("checksum");
}
For how to convert your checksum into a byte array, see this question:
How can I convert a hex string to a byte array?
You can count a number of elements by getting a length of your collection:
int ElementsCount = nodes.Count;
You can read attributes as following:
foreach(XmlNode node in nodes) {
Console.WriteLine("Value: " + node.Attributes["name_of_attribute"].Value;
}
EDIT:
you should be able to use nodes[0].ChildNodes.Count;.
El Padrino showed a solution:
How to change XML Attribute
where an xml element can be loaded directly (no for each..), edited and saved!
My xml is:
<?xml version="1.0" encoding="ISO-8859-8"?>
<g>
<page no="1" href="page1.xml" title="נושא 1">
<row>
<pic pos="1" src="D:\RuthSiteFiles\webSiteGalleryClone\ruthCompPics\C_WebBigPictures\100CANON\IMG_0418.jpg" width="150" height="120">1</pic>
</row>
</page>
</g>
and I need to select a node by two attributes(1. "no" in the page tag and "pos" in the pic tag)
I've found :
How to access a xml node with attributes and namespace using selectsinglenode()
where direct access is possible but beside the fact that I dont understand the solution, I think it uses the xpath object which can't be modified and save changes.
What's the best way to
access directly an xml node (I'm responsible that the node will be unique)
edit that node
save changes to the xml
Thanks
Asaf
You can use the same pattern as the first answer you linked to, but you will need to include the conditions on the attributes in the XPath. Your basic XPath would be g/page/row/pic. Since you want the no attribute of page to be 1, you add [#no='1'] as a predicate on page. So, the full XPath query is something like g/page[#no='1']/row/pic[#pos='1']. SelectSingleNode will return a mutable XmlNode object, so you can modify that object and save the original document to save changes.
Putting the XPath together with El Padrino's answer:
//Here is the variable with which you assign a new value to the attribute
string newValue = string.Empty;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlFile);
XmlNode node = xmlDoc.SelectSingleNode("g/page[#no='1']/row/pic[#pos='1']");
node.Attributes["src"].Value = newValue;
xmlDoc.Save(xmlFile);
//xmlFile is the path of your file to be modified
Use the new, well-designed XDocument/XElement instead of the old XmlDocument API.
In your example,
XDocument doc = XDocument.Load(filename);
var pages = doc.Root.Elements("page").Where(page => (int?) page.Attribute("no") == 1);
var rows = pages.SelectMany(page => page.Elements("row"));
var pics = rows.SelectMany(row => row.Elements("pic").Where(pic => (int?) pic.Attribute("pos") == 1));
foreach (var pic in pics)
{
// outputs <pic pos="1" src="D:\RuthSiteFiles\webSiteGalleryClone\ruthCompPics\C_WebBigPictures\100CANON\IMG_0418.jpg" width="150" height="120">1</pic>
Console.WriteLine(pic);
// outputs 1
Console.WriteLine(pic.Value);
// Changes the value
pic.Value = 2;
}
doc.Save(filename);