I have a number of xml documents from which I want to retrieve all of the entity references and perform some updates on the documents. I am using an XmlDocument object to do the updates. What is the best way to retrieve all entity references from an XmlDocument object?
This may not be the best or most efficient solution, but in light of the fact that I did not receive any other answers, this is what I came up with:
XmlTextReader reader = new XmlTextReader(stream);
reader.EntityHandling = EntityHandling.ExpandCharEntities;
XmlDocument doc = new XmlDocument();
doc.Load(reader);
List<XmlEntityReference> entityRefs = new List<XmlEntityReference>();
RetrieveEntityRefs(doc.DocumentElement, doc, entityRefs);
private void RetrieveEntityRefs(XmlNode parentNode, XmlDocument doc, List<XmlEntityReference> entityReferences) {
foreach (XmlNode node in parentNode.ChildNodes)
{
if (node.NodeType == XmlNodeType.EntityReference) {
XmlEntityReference entityRef = node as XmlEntityReference;
entityReferences.Add(entityRef);
}
else if (node.HasChildNodes) {
RetrieveEntityRefs(node, doc, entityReferences);
}
}
}
Related
I've read the other question and apply the answer to my code, but it still doesn't work.
I use xml to read the csproj file to read/write something into it body.
This is my csproject file if anyone care!
My code:
static void Main(string[] args)
{
string file = #"D:\Project\svn_longnx\LearnSvnRevision\ConsoleApp1\WindowsFormsApp1\WindowsFormsApp1.csproj";
XmlDocument xdoc = new XmlDocument();
xdoc.Load(file);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("default", "http://schemas.microsoft.com/developer/msbuild/2003");
XmlElement root = xdoc.DocumentElement;
XmlNode node = root.SelectSingleNode("Project/PropertyGroup/ApplicationVersion",nsmgr);
Console.WriteLine(node.Value);
}
This dummy code failed either:
static void Main(string[] args)
{
string file = #"D:\Project\svn_longnx\LearnSvnRevision\ConsoleApp1\WindowsFormsApp1\WindowsFormsApp1.csproj";
XmlDocument xdoc = new XmlDocument();
xdoc.Load(file);
XmlElement root = xdoc.DocumentElement;
XmlNode node = root.SelectSingleNode("Project/PropertyGroup/ApplicationVersion");
Console.WriteLine(node.Value);
}
It's much easier to use XDocument and associated objects instead of the older XmlDocument bits. Also, you're probably falling down because of namespaces. Instead, try this code:
var doc = XDocument.Load(#"D:\Project\svn_longnx\Lear...\WindowsFormsApp1.csproj");
var ns = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");
var applicationVersionElements = doc.Element(ns + "Project")
.Descendants(ns + "PropertyGroup")
.Descendants(ns + "ApplicationVersion");
foreach (var element in applicationVersionElements)
{
Console.WriteLine(element.Name);
}
This is just one way as an example of how to get that specific element to demonstrate how easy it is to use.
Note, you may need to add using System.Xml.Linq;
hello I have this xml code
<AriaGostarInformation>
<MenuInformation>
<MenuNames Name="1">
home
</MenuNames>
<MenuNames Name="2">
pages
</MenuNames>
<MenuNames Name="3">
blog
</MenuNames>
<MenuNames Name="4">
gallery
</MenuNames>
<MenuNames Name="5">
about us
</MenuNames>
<MenuNames Name="6">
contact us
</MenuNames>
<SubMenuNames parentName="1">
fgfgfgfgs
</SubMenuNames>
<SubMenuNames parentName="3">
</SubMenuNames>
</MenuInformation>
</AriaGostarInformation>
and c# code:
public void updateXmlNodeWithAttribute(string XmlNodeName, string XmlAttributeName, List<string> XmlNodeAttribute, List<string> XmlNodeValue)
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(_connection);
XmlNodeList xnode = getXmlNodeList(XmlNodeName);
for (int i = 0; i < XmlNodeAttribute.Count - 1; i++)
{
foreach (XmlNode item in xnode)
{
if (item.Attributes[XmlAttributeName].Value == XmlNodeAttribute[i].ToString())
{
item.InnerText = XmlNodeValue[i].ToString();
}
}
}
xdoc.Save(_connection);
}
The getXmlNodeList method return Specified xmlnodelist from XML file
This code does not throw an error, but DOES NOT update the XML node.
public XmlNodeList getXmlNodeList(string XmlNodeName)
{
XmlDocument doc = new XmlDocument();
doc.Load(_connection);
XmlNodeList elemList = doc.GetElementsByTagName(XmlNodeName);
return elemList;
}
Please help me where is error in my code?
I found two problems in your code.
The first error is in your for loop. With -1 the last element in the List is always not used. Or if you only have one element in your list the for loop is never executed.
The second problem is, that you load the XML file twice. First time you load it in the updateXmlNodeWithAttribute method. Second time you load it in the getXmlNodeList method.
You must know that each loaded XML Document is independent from other loaded XML Document.
So what your code does is you get a List with XML Nodes from your XML file loaded in the getXmlNodeList Method, edit one of these XML Nodes and then you try to save the XML Document loaded in the updateXmlNodeWithAttribute method where no changes were made because the two XML Documents are completely independent.
So what you have to do is use the same XmlDocument object in both methods.
See the code below.
public static void updateXmlNodeWithAttribute(string XmlNodeName, string XmlAttributeName, List<string> XmlNodeAttribute, List<string> XmlNodeValue)
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(_connection);
XmlNodeList xnode = getXmlNodeList(XmlNodeName,xdoc);
for (int i = 0; i < XmlNodeAttribute.Count; i++)
{
foreach (XmlNode item in xnode)
{
if (item.Attributes[XmlAttributeName].Value == XmlNodeAttribute[i].ToString())
{
item.InnerText = XmlNodeValue[i].ToString();
}
}
}
xdoc.Save(_connection);
}
public static XmlNodeList getXmlNodeList(string XmlNodeName, XmlDocument doc)
{
XmlNodeList elemList = doc.GetElementsByTagName(XmlNodeName);
return elemList;
}
I'm creating a class for manipulate XML, I created overload of my method RemoveNode
public partial class HWXml
{
public string XmlFile;
private XmlDocument XmlDoc = new XmlDocument();
public HWXml(string XmlFile)
{
this.XmlFile = XmlFile;
}
public XmlNode SelectSingleNode(string NodePath)
{
XmlDoc.Load(XmlFile);
return XmlDoc.SelectSingleNode(NodePath);
}
public void RemoveNode(XmlNode removeChild)
{
XmlDoc.Load(XmlFile);
removeChild.ParentNode.RemoveChild(removeChild);
XmlDoc.Save(XmlFile);
}
public void RemoveNode(string RemoveChild)
{
XmlDoc.Load(XmlFile);
XmlNode removeChild = XmlDoc.SelectSingleNode(RemoveChild);
removeChild.ParentNode.RemoveChild(removeChild);
XmlDoc.Save(XmlFile);
}
}
When I try remove node using string parameter, it works
private void RemoveXML_Click(object sender, RoutedEventArgs e)
{
MyXmlClass myXmlClass = new MyXmlClass(XmlFile);
myXmlClass.RemoveNode("root/Content");
}
But when I try remove node using XmlNode parameters, it will compile, execute, no error message, but no effect, it no remove nothing in the XML file.
private void RemoveXML_Click(object sender, RoutedEventArgs e)
{
MyXmlClass myXmlClass = new MyXmlClass(XmlFile);
XmlNode node = myXmlClass.SelectSingleNode("root/Conteudo");
myXmlClass.RemoveNode(node);
}
What is problem?
XmlNode parameter is definitely not part of XmlDoc which you are loading inside this method (because you have this node before document is loaded). Thus manipulations on this node do not affect document which node does not belong to.
In second case you are selecting node of document which was loaded. This node belongs to xml tree which was just loaded into XmlDoc, thus removing node affects document.
What you should understand is how XmlDocument (same for XDocument) is loaded:
If it has some nodes (previously loaded) then all nodes are removed
XmlReader created
This reader reads input stream node by node
For each found node, new instance of appropriate class is created and added to document (e.g. if reader has read some element, then new XmlElement is created and added to current element of document)
So, you end up with graph of completely new objects which have no relation to objects which was created during previous load of xml. After loading xml, instance of XmlDocument stays same, but it has completely new objects inside.
I have an XML file with no root. I cannot change this. I am trying to parse it, but XDocument.Load won't do it. I have tried to set ConformanceLevel.Fragment, but I still get an exception thrown. Does anyone have a solution to this?
I tried with XmlReader, but things are messed up and can't get it work right. XDocument.Load works great, but if I have a file with multiple roots, it doesn't.
XmlReader itself does support reading of xml fragment - i.e.
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var reader = XmlReader.Create("fragment.xml", settings))
{
// you can work with reader just fine
}
However XDocument.Load does not support reading of fragmented xml.
Quick and dirty way is to wrap the nodes under one virtual root before you invoke the XDocument.Parse. Like:
var fragments = File.ReadAllText("fragment.xml");
var myRootedXml = "<root>" + fragments + "</root>";
var doc = XDocument.Parse(myRootedXml);
This approach is limited to small xml files - as you have to read file into memory first; and concatenating large string means moving large objects in memory - which is best avoided.
If performance matters you should be reading nodes into XDocument one-by-one via XmlReader as explained in excellent #Martin-Honnen 's answer (https://stackoverflow.com/a/18203952/2440262)
If you use API that takes for granted that XmlReader iterates over valid xml, and performance matters, you can use joined-stream approach instead:
using (var jointStream = new MultiStream())
using (var openTagStream = new MemoryStream(Encoding.ASCII.GetBytes("<root>"), false))
using (var fileStream =
File.Open(#"fragment.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
using (var closeTagStream = new MemoryStream(Encoding.ASCII.GetBytes("</root>"), false))
{
jointStream.AddStream(openTagStream);
jointStream.AddStream(fileStream);
jointStream.AddStream(closeTagStream);
using (var reader = XmlReader.Create(jointStream))
{
// now you can work with reader as if it is reading valid xml
}
}
MultiStream - see for example https://gist.github.com/svejdo1/b9165192d313ed0129a679c927379685
Note: XDocument loads the whole xml into memory. So don't use it for large files - instead use XmlReader for iteration and load just the crispy bits as XElement via XNode.ReadFrom(...)
The only in-memory tree representations in the .NET framework that can deal with fragments are the XmlDocumentFragment in .NET's DOM implementation so you would need to create an XmlDocument and a fragment with e.g.
XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = stringWithXml; // for instance
// frag.InnerXml = File.ReadAllText("fragment.xml");
or is XPathDocument where you can create one using an XmlReader with ConformanceLevel set to Fragment:
XPathDocument doc;
using (XmlReader xr =
XmlReader.Create("fragment.xml",
new XmlReaderSettings()
{
ConformanceLevel = ConformanceLevel.Fragment
}))
{
doc = new XPathDocument(xr);
}
// new create XPathNavigator for read out data e.g.
XPathNavigator nav = doc.CreateNavigator();
Obviously XPathNavigator is read-only.
If you want to use LINQ to XML then I agree with the suggestions made that you need to create an XElement as a wrapper. Instead of pulling in a string with the file contents you could however use XNode.ReadFrom with an XmlReader e.g.
public static class MyExtensions
{
public static IEnumerable<XNode> ParseFragment(XmlReader xr)
{
xr.MoveToContent();
XNode node;
while (!xr.EOF && (node = XNode.ReadFrom(xr)) != null)
{
yield return node;
}
}
}
then
XElement root = new XElement("root",
MyExtensions.ParseFragment(XmlReader.Create(
"fragment.xml",
new XmlReaderSettings() {
ConformanceLevel = ConformanceLevel.Fragment })));
That might work better and more efficiently than reading everything into a string.
If you wanted to use XmlDocument.Load() then you would need to wrap the content in a root node.
or you could try something like this...
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
XmlDocument d = new XmlDocument();
d.CreateElement().InnerText = xmlReader.ReadOuterXml();
}
}
XML document cannot have more than one root elements. One root element is required. You may do one thing. Get all the fragment elements and wrap them into a root element and parse it with XDocument.
This would be the best and easiest approach that one could think of.
I've created a [WebMethod] on ASP.NET Web Service which reads XML documents from different web services (ASP.NET and PHP services).
Once the documents are read, they are merged into a XML and returned.
The method can successfully read XML documents from each web service but when I try to append second XML into first one, I get ArgumentException and a message like this The node to be inserted is from a different document context. I can't find a problem, can it be something to do with the source of document? but then both document are exactly same (have same elements).
Why ArgumentException? What am I missing?
[WebMethod]
public XmlDocument getRestaurants(String search_keywords)
{
XmlDocument xmlDom1 = new XmlDocument();
xmlDom1 = getRestaurantFromAspNetWS(search_keywords);
XmlTextReader myXmlTextReader =
new XmlTextReader
("http://some-iss.green.com/username/search.php?s=" + search_keywords);
XmlDocument xmlDom2 = new XmlDocument();
xmlDom2.Load(myXmlTextReader);
foreach (XmlElement xmlNode in xmlDom2.DocumentElement.ChildNodes)
{
//trying to append childNodes of xmlDom2 into xmlDom1
//and this is where i get ArgumentException
xmlDom1.DocumentElement.AppendChild(xmlNode);
}
return xmlDom1;
}
You need to import the node with importNode()
something like this:
xmlDom1.DocumentElement.importNode(xmlNode, true);
Can you write it like this?
public XElement getRestaurants(String search_keywords)
{
XElement result = getRestaurantFromAspNetWS(search_keywords);
XmlTextReader myXmlTextReader = new XmlTextReader
("http://some-iss.green.com/username/search.php?s=" + search_keywords);
XElement reader = XElement.Load(myXmlTextReader);
foreach (XElement child in reader.Elements())
result.Add(child);
return result;
}
The real trick is getting your custom getRestaurantFromAspNetWS function to return a XElement instead of a XmlDocument, as you didn't provide us with that function, I can't help you there.