Read Large XML file using XPath - c#

I am trying to read windows update package.xml file which is roughly 65mb in size, I am trying to just grab the URL attribute using Xpath but for some odd reason my object always returns empty. Here is my code:
doc.Load(#".\package.xml");
string xpath= "/OfflineSyncPackage/FileLocations/FileLocation/#Url";
XmlNodeList nodeList2 = doc.SelectNodes(xpath);
I have also tried using XmlReader which is also not working for me:
string packXML = #".\package.xml";
using (XmlReader xr = XmlReader.Create(packXML))
{
while (xr.Read())
{
switch (xr.NodeType)
{
case XmlNodeType.Element:
if (xr.Name == "OfflineSyncPackage")
{
xr.ReadStartElement("FileLocations");
if (xr.Name == "FileLocations")
{
if (xr.Name == "FileLocation")
{
}
}
}
break;
}
}
}
The package.xml file can be found in package.cab which is in this file: http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab
What is the best way to do this as I do not want to load whole file into memory due to size
Any advice is appreciated! Thank you

I figured this out finally!
public void ParseXML(string XMLPath)
{
XmlReader xmlReader = XmlReader.Create(XMLPath);
while (xmlReader.Read())
{
if (xmlReader.Name.Equals("FileLocation") && (xmlReader.NodeType == XmlNodeType.Element))
{
string url = xmlReader.GetAttribute("Url");
}
}
}

Related

Can't load XML from Resource/Drawable/

I've saved my Xml in Resource/Drawable/ . I put it here cause is the only place where it can stay without any error.
Any way this is the exception:
System.IO.FileNotFoundException: Could not find file "/sds".
How can I find the path?
Here is my code:
String conditionName = "";
XDocument xml = XDocument.Load("#drawable/LightConditions.xml");
foreach (XElement item in xml.Root.Elements("Condizione"))
{
if (item.Element("EV").Value == EV)
{
conditionName = item.Parent.Element("Nome").Value;
break;
}
//do something
}
You will want to place that .xml in the Assets folder and flag the build action as an AndroidAsset.
Then you can use the AssetManager to access it via a Stream:
string conditionName = "";
using (StreamReader sr = new StreamReader(this.Assets.Open("LightConditions.xml")))
{
XDocument xml = XDocument.Load(sr);
foreach (XElement item in xml.Root.Elements("Condizione"))
{
if (condizione.Element("EV").Value == EV)
{
conditionName = item.Parent.Element("Nome").Value;
break;
}
//do something
}
}
Consult the Xamarin.Android guide on Assets

How do i read xml file on dropbox with XmlTextReader

I am trying to make my winform application read an xml file stored on dropbox. But when it gets to
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "updater"))
Then it wont continue. but if i remove && (reader.Name == "updater") then it continues but it still dont read the value of "version" and "url".
Link to update.xml: https://www.dropbox.com/s/25gcpllsqsj1qrq/update.xml
Below is my C# code in my project. I am trying to read the update.xml on dropbox with XmlTextReader, but i just never get any of the values back!
I tryed to place messageboxes to show me reader.Name, but it returns "html" instead of "updater".
It dont necessary have to be XmlTextReader. As long as the solution works, then its fine :D
Any help will be appreciated.
private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
{
Version newVersion = null;
string url = "";
XmlTextReader reader = null;
try
{
string xmlURL = "https://www.dropbox.com/s/25gcpllsqsj1qrq/update.xml";
reader = new XmlTextReader(xmlURL);
reader.MoveToContent();
string elementName = "";
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "updater"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element) elementName = reader.Name;
else
{
if ((reader.NodeType == XmlNodeType.Text) && (reader.HasValue))
{
switch (elementName)
{
case "version":
newVersion = new Version(reader.Value);
break;
case "url":
url = reader.Value;
break;
}
}
}
}
}
}
catch (Exception)
{
}
finally
{
if (reader != null) reader.Close();
}
Version curVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (curVersion.CompareTo(newVersion) < 0)
{
string title = "New version detected.";
string question = "Download the new version?";
if (DialogResult.Yes == MessageBox.Show(this, question, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question))
{
System.Diagnostics.Process.Start(url);
}
}
else
{
MessageBox.Show("The program is up to date!");
}
}
Well, going to the Url provided doesn't give you the XML file as you might expect, it's actually giving you the content through a Dropbox wrapper. So when you're doing your read, you're actually hitting the HTML page. You can double-check this by putting a break point on the elementName for each iteration.
This is the correct URL https://dl.dropboxusercontent.com/s/25gcpllsqsj1qrq/update.xml?token_hash=AAFnI4T9nSCGeVgDKLm7YB79dbjNtoYkAj_mChxVDZL0AQ&dl=1 (got it from the Download button), here give this a try:
string xmlURL = "https://dl.dropboxusercontent.com/s/25gcpllsqsj1qrq/update.xml?token_hash=AAFnI4T9nSCGeVgDKLm7YB79dbjNtoYkAj_mChxVDZL0AQ&dl=1";
This is an alternative way to load the contents to a XDocument and further query it with LINQ:
string url = "https://dl.dropboxusercontent.com/s/25gcpllsqsj1qrq/update.xml?token_hash=AAFnI4T9nSCGeVgDKLm7YB79dbjNtoYkAj_mChxVDZL0AQ&dl=1";
WebRequest request = HttpWebRequest.Create(url);
using (WebResponse response = request.GetResponse())
using (Stream stream = response.GetResponseStream())
{
var doc = XDocument.Load(stream);
// do your magic (now it's a valid XDocument)
}

System.IO exception coming while saving XML document in C#

After importing plenty of XML files into application i tried to do modifications on it by using XML document class, for this i created few methods to do modifications.
The thing is the starting method it's working fine and when comes to the second one it's displaying System.IO exception like "File is already using another process".
So any one help me out how can i solve this issue.
Sample code what i'm doing:
Method1(fileList);
Method2(fileList);
Method3(fileList);
private void Method1(IList<RenamedImportedFileInfo> fileList)
{
try
{
string isDefaultAttribute = Resource.Resources.ImportIsDefaultAttribute;
string editorsPath = editorsFolderName + Path.DirectorySeparatorChar + meterType;
string profilesPath = profileFolderName + Path.DirectorySeparatorChar + meterType;
string strUriAttribute = Resource.Resources.ImportUriAttribute;
foreach (RenamedImportedFileInfo renameInfo in fileList)
{
if (renameInfo.NewFilePath.ToString().Contains(editorsPath) && (renameInfo.IsProfileRenamed != true))
{
var xmldoc = new XmlDocument();
xmldoc.Load(renameInfo.NewFilePath);
if (xmldoc.DocumentElement.HasAttribute(isDefaultAttribute))
{
xmldoc.DocumentElement.Attributes[isDefaultAttribute].Value = Resource.Resources.ImportFalse;
}
XmlNodeList profileNodes = xmldoc.DocumentElement.GetElementsByTagName(Resource.Resources.ImportMeasurementProfileElement);
if (profileNodes.Count == 0)
{
profileNodes = xmldoc.DocumentElement.GetElementsByTagName(Resource.Resources.ImportBsMeasurementProfileElement);
}
if (profileNodes.Count > 0)
{
foreach (RenamedImportedFileInfo profileName in oRenamedImportedFileList)
{
if (profileName.NewFilePath.ToString().Contains(profilesPath))
{
if (string.Compare(Path.GetFileName(profileName.OldFilePath), Convert.ToString(profileNodes[0].Attributes[strUriAttribute].Value, CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase) == 0)
{
profileNodes[0].Attributes[strUriAttribute].Value = Path.GetFileName(profileName.NewFilePath);
renameInfo.IsProfileRenamed = true;
break;
}
}
}
}
xmldoc.Save(renameInfo.NewFilePath);
xmldoc = null;
profileNodes = null;
}
}
oRenamedImportedFileList = null;
}
catch (NullReferenceException nullException) { LastErrorMessage = nullException.Message; }
}
Thanks,
Raj
You are probably opening the same file twice in your application. Before you can open it again, you have to close it (or leave it open and work on the same document without opening it again).
For help on how to implement this, please show us more code so we can give you advice.

View all text of an element with XmlReader C#

I'm using an XmlReader to iterate through some XML. Some of the XML is actually HTML and I want to get the text content from the node.
Example XML:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<p>Here is some <b>data</b></p>
</data>
Example code:
using (XmlReader reader = new XmlReader(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
// I want to get all the TEXT contents from the this node
myVar = reader.Value;
}
}
}
This doesn't get me all the contents. How do I get all the contents from the node in that situation?
Use ReadInnerXml:
StringReader myUrl = new StringReader(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<data>
<p>Here is some <b>data</b></p>
</data>");
using (XmlReader reader = XmlReader.Create(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
// I want to get all the TEXT contents from the this node
Console.WriteLine(reader.ReadInnerXml());
}
}
}
Or if you want to skip the <b> as well, you can use an aux reader for the subtree, and only read the text nodes:
StringReader myUrl = new StringReader(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<data>
<p>Here is some <b>data</b></p>
</data>");
StringBuilder myVar = new StringBuilder();
using (XmlReader reader = XmlReader.Create(myUrl))
{
while (reader.Read())
{
if (reader.Name == "p")
{
XmlReader pReader = reader.ReadSubtree();
while (pReader.Read())
{
if (pReader.NodeType == XmlNodeType.Text)
{
myVar.Append(pReader.Value);
}
}
}
}
}
Console.WriteLine(myVar.ToString());
I can't upvote or comment on others' responses, so let me just say carlosfigueira hit the nail on the head, that's exactly how you read the text value of an element. his answer helped me immensely.
for the sake of exmeplification here's my code:
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
{
if (reader.Name == "CharCode")
{
switch (reader.ReadInnerXml())
{
case "EUR":
{
reader.ReadToNextSibling("Value");
label4.Text = reader.ReadInnerXml();
}
break;
case "USD":
{
reader.ReadToNextSibling("Value");
label3.Text = reader.ReadInnerXml();
}
break;
case "RUB":
{
reader.ReadToNextSibling("Value");
label5.Text = reader.ReadInnerXml();
}
break;
case "RON":
{
reader.ReadToNextSibling("Value");
label6.Text = reader.ReadInnerXml();
}
break;
}
}
}
break;
}
}
the file I'm reading can be found here: http://www.bnm.md/md/official_exchange_rates?get_xml=1&date=
(you have to add a date in DD.MM.YYYY format to it to get the .XML)
I suggest you use HtmlAgilityPack which is a mature and, stable library for doing this sort of thing. It takes care of fetching the html, converting it to xml, and allows you to select the nodes you'd like with XPATH.
In your case it would be as simple as executing
HtmlDocument doc = new HtmlWeb().Load(myUrl);
string text = doc.DocumentNode.SelectSingleNode("/data/p").InnerText;

XmlReader - I need to edit an element and produce a new one

I am overriding a method which has an XmlReader being passed in, I need to find a specific element, add an attribute and then either create a new XmlReader or just replace the existing one with the modified content. I am using C#4.0
I have investigated using XElement (Linq) but I can't seem to manipulate an existing element and add an attribute and value.
I know that the XmlWriter has WriteAttributeString which would be fantastic but again I am not sure how it all fits together
I would like to be able to do something like --- This is pseudo-code!
public XmlReader DoSomethingWonderful(XmlReader reader)
{
Element element = reader.GetElement("Test");
element.SetAttribute("TestAttribute","This is a test");
reader.UpdateElement(element);
return reader;
}
XmlReader/Writer are sequential access streams. You will have to read in on one end, process the stream how you want, and write it out the other end. The advantage is that you don't need to read the whole thing into memory and build a DOM, which is what you'd get with any XmlDocument-based approach.
This method should get you started:
private static void PostProcess(Stream inStream, Stream outStream)
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " };
using (var reader = XmlReader.Create(inStream))
using (var writer = XmlWriter.Create(outStream, settings)) {
while (reader.Read()) {
switch (reader.NodeType) {
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.Name, reader.NamespaceURI);
writer.WriteAttributes(reader, true);
//
// check if this is the node you want, inject attributes here.
//
if (reader.IsEmptyElement) {
writer.WriteEndElement();
}
break;
case XmlNodeType.Text:
writer.WriteString(reader.Value);
break;
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
break;
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
writer.WriteProcessingInstruction(reader.Name, reader.Value);
break;
case XmlNodeType.SignificantWhitespace:
writer.WriteWhitespace(reader.Value);
break;
}
}
}
}
This is not quite as clean as deriving your own XmlWriter, but I find that it's much easier.
[EDIT]
An example of how you would open two streams at once might be something like this:
using (FileStream readStream = new FileStream(#"c:\myFile.xml", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write)) {
using (FileStream writeStream = new FileStream(#"c:\myFile.xml", FileMode.OpenOrCreate, FileAccess.Write)) {
PostProcess(readStream, writeStream);
}
}
I fixed it using the following duct tape coding
public XmlReader FixUpReader(XmlReader reader)
{
reader.MoveToContent();
string xml = reader.ReadOuterXml();
string dslVersion = GetDSLVersion();
string Id = GetID();
string processedValue = string.Format("<ExampleElement dslVersion=\"{1}\" Id=\"{2}\" ", dslVersion, Id);
xml = xml.Replace("<ExampleElement ", processedValue);
MemoryStream ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(xml));
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader myReader = XmlReader.Create(ms);
myReader.MoveToContent();
return myReader;
}
I feel dirty for doing it this way but it is working....
You can't easily do this with XmlReader - at least, not without reading the whole XML document in from the reader, futzing with it and then creating a new XmlReader from the result. That defeats a lot of the point of using XmlReader though - namely the ability to stream large documents.
You could potentially derive from XmlReader, forwarding most method calls to the existing reader but intercepting them where appropriate to add extra attributes etc... but I suspect that code would be really quite complex and fragile.
I would rather prefer to load the xml into XmlDocument object and use Attributes collection to modify the value and call Save method to update this value. Below code works for me.
public static void WriteElementValue ( string sName, string element, string value)
{
try
{
var node = String.Format("//elements/element[#name='{0}']", sName);
var doc = new XmlDocument { PreserveWhitespace = true };
doc.Load(configurationFileName);
var nodeObject = doc.SelectSingleNode(node);
if (nodeObject == null)
throw new XmlException(String.Format("{0} path does not found any matching
node", node));
var elementObject = nodeObject[element];
if (elementObject != null)
{
elementObject.Attributes["value"].Value = value;
}
doc.Save(configurationFileName);
}
catch (Exception ex)
{
throw new ExitLevelException(ex, false);
}
}
I've also observed when you use XmlWriter or XmlSerializer the whitespaces were not correctly preserved, this could be annoying sometimes
string newvalue = "10";
string presentvalue = "";
string newstr = "";
XmlReader xmlr = XmlReader.Create(new StringReader(str));
while (xmlr.Read())
{
if (xmlr.NodeType == XmlNodeType.Element)
{
if (xmlr.Name == "priority")
{
presentvalue = xmlr.ReadElementContentAsString();
newstr = str.Replace(presentvalue, newvalue);
}
}
}
//newstr can be written back to file... that is the edited xml

Categories