I'm trying to
Load external xml
Select only "oldName" nodes
Change "oldName" nodes to "newName" nodes
Create and save new xml file
The code below does not work, it only creates "items" node.
// Load xml
XElement doc = XElement.Load("http://.../file.xml");
var nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName"));
foreach (var element in nodes)
{
if (element.Name.LocalName.Equals("oldName"))
{
element.Name = "newName";
}
}
XElement newDoc = new XElement("item", nodes);
// Save xml
newDoc.Save("C:/newFile.xml");
Any help would be appreciated.
EDIT: Selecting "oldName" nodes works, but I need to rename them also.
// Load xml
XElement doc = XElement.Load("http://.../file.xml");
var nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName"));
XElement newDoc = new XElement("item", nodes);
// Save xml
newDoc.Save("C:/newFile.xml");
EDIT: Added file example
file.xml
<ignoreThisElement>
<oldName>
...
</oldName>
</ignoreThisElement>
newFile.xml
<newName>
...
</newName>
The reason why your code only creates an empty "items" node is because the 'nodes' var no longer contains elements with the old name. You changed all the elements to the new variable, but the linq query still references the old name.
// Load xml
XElement doc = XElement.Load("http://.../file.xml");
//using linq to get all nodes with name 'oldName'
var nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName"));
//you rename the nodes here, so the linq query no longer finds nodes with 'oldName'
foreach (var element in nodes)
{
if (element.Name.LocalName.Equals("oldName"))
{
element.Name = "newName";
}
}
//result is 'items' node with no children
XElement newDoc = new XElement("item", nodes);
// Save xml
newDoc.Save("C:/newFile.xml");
The solution would be for your to extract the required nodes first, then move them to your newDoc element. Thereafter you can rename the nodes:
// Load xml
XElement doc = XElement.Load("http://.../file.xml");
var nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName"));
//move the extracted old nodes to your newDoc first
XElement newDoc = new XElement("item", nodes);
//now rename
foreach (var element in newDoc.Descendants())
{
if (element.Name.LocalName.StartsWith("oldName"))
{
element.Name = "newName";
}
}
// Save xml
newDoc.Save("C:/newFile.xml");
Alternatively, you could requery the doc variable for the new node names after renaming them:
// Load xml
XElement doc = XElement.Load("http://.../file.xml");
//using linq to get all nodes with name 'oldName'
var nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName"));
foreach (var element in nodes)
{
if (element.Name.LocalName.Equals("oldName"))
{
element.Name = "newName";
}
}
//get all newName nodes
var newNodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("newName"));
XElement newDoc = new XElement("item", newNodes);
// Save xml
newDoc.Save("C:/newFile.xml");
You can try something like this
// Load the xml
var doc = XDocument.Load("http://.../file.xml");
// loop thru the element an d search for a specific node and change the name
foreach (var element in doc.Descendants())
{
if (element.Name.LocalName.StartsWith("oldName"))
{
element.Name = "newName";
}
}
// Save xml
doc.Save("C:/newFile.xml");
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication3
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XElement doc = XElement.Load(FILENAME);
List<XElement> nodes = doc.Descendants().Where(element => element.Name.LocalName.Equals("oldName")).ToList();
for(int index = nodes.Count() - 1; index >= 0; index--)
{
XElement node = nodes[index];
node.ReplaceWith(new XElement("newName", node.Descendants()));
}
}
}
}
Related
How do we iterate an XmlDocument using an xpath?
I'm attempting to return a list of nodes by xpath:
public static List<string> Filter(string xpath, string input, string ns, string nsUrl)
{
var bytes = Encoding.UTF8.GetBytes(input); //i believe this unescapes the string
var stream = new MemoryStream(bytes);
var doc = new XmlDocument();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
namespaceManager.AddNamespace(ns, nsUrl);
var links = new List<string>();
var nodes = doc.SelectNodes(xpath, namespaceManager);
using (var reader = new XmlTextReader(stream))
{
reader.Namespaces = false;
doc.Load(reader);
}
foreach (XmlNode node in nodes)
{
if (IsNullOrWhiteSpace(node.InnerText))
{
continue;
}
links.Add(node.InnerText);
}
return links;
}
however, the count is always 0 !
I'm using this xpath. notice how i am using only 1 namespace:
/ns0:Visit/ns0:DocumentInterface/ns0:Documents/ns0:Document/ns0:BinaryData
The header of the file looks like this:
<ns0:Visit xmlns:ns0="http://NameSpace.ExternalSchemas.Patient"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
I'm certain that I am using the right xpath because I tested it against my payload:
I'm calling the function this way:
var links = Filter(xpath, xml, "ns0", "http://NameSpace.ExternalSchemas.Patient");
How do we iterate an XmlDocument using an xpath? Perhaps the XmlDocument should be an XDocument instead?
I have a list of XML files that I need to extract 3 values from each file.
The XML looks somewhat like :
<ClinicalDocument xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" moodCode="EVN" xmlns="urn:hl7-org:v3">
<title>Summary</title>
<recordTarget>
<patientRole>
<patient>
<name>
<given>John</given>
<given>S</given>
<family>Doe</family>
</name>
<birthTime value="19480503" />
I'm trying to extract given name, family name and birth time.
Initially I'm trying to print out the values using:
XmlDocument doc2 = new XmlDocument();
doc2.Load(#"Z:\\DATA\\file.XML");
XmlElement root = doc2.DocumentElement;
XmlNodeList list = root.GetElementsByTagName("name");
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list.Item(i).Value);
}
I'm not getting any value printed, but when I debug and check the inner values of "list" I can see what I need from that tag.
How can I extract the needed information?
Your code and all other answers ignore the default namespace xmlns="urn:hl7-org:v3"
I find Linq2Xml easier to use, so I'll post an answer using it..
var xDoc = XDocument.Load(filename);
var #namespace = "urn:hl7-org:v3";
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xDoc.CreateNavigator().NameTable);
namespaceManager.AddNamespace("ns", #namespace);
XNamespace ns = #namespace;
var names = xDoc.XPathSelectElements("//ns:patient/ns:name", namespaceManager).ToList();
var list = names.Select(p => new
{
Given = string.Join(", ", p.Elements(ns + "given").Select(x => (string)x)),
Family = (string)p.Element(ns + "family"),
BirthTime = new DateTime(1970,1,1).AddSeconds( (int)p.Parent.Element(ns + "birthTime").Attribute("value"))
})
.ToList();
Try this instead:
XmlDocument doc2 = new XmlDocument();
doc2.Load(#"Path\To\XmlFile.xml");
XmlElement root = doc2.DocumentElement;
XmlNodeList list = root.GetElementsByTagName("name");
var names = list[0].ChildNodes;
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine(names[i].InnerText);
}
Output:
John
S
Doe
There are 2 issues with your code:
The first being that you were iterating around the name element, which only has a Count of 1 (as there is only one of these). That's why I included list[0],ChildNodes, to get all the children of the name element (given, given and family).
To retrieve the text inside each element, ("John", "S", "Doe"), you should use InnerText instead of Value
It's not clear from your example XML if there is only ever one <name> element or if there could be multiple. The following assumes there might be multiple. It also grabs the birthdate.
for (int i = 0; i < list.Count; i++)
{
var xmlNode = list.Item(i).FirstChild;
while (xmlNode != null)
{
Console.WriteLine(xmlNode.InnerText);
xmlNode = xmlNode.NextSibling;
}
}
XmlNodeList birthDates = root.GetElementsByTagName("birthTime");
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(birthDates[i].Attributes["value"].Value);
}
If there are multiple <patient> elements in your xml you could do:
using System;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
class Program
{
static void Main()
{
var doc = XDocument.Load("a.xml");
var nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("x", "urn:hl7-org:v3");
var patients = doc.XPathSelectElements("//x:patient", nsm);
foreach (var patient in patients)
{
Console.WriteLine(patient.XPathSelectElement("./x:name/x:given[1]", nsm).Value);
Console.WriteLine(patient.XPathSelectElement("./x:name/x:given[2]", nsm).Value);
Console.WriteLine(patient.XPathSelectElement("./x:name/x:family", nsm).Value);
Console.WriteLine(patient.XPathSelectElement("./x:birthTime", nsm).Attribute("value").Value);
}
}
}
Why do you need to add the name space explicitly even if it's a default name space in the xml? see: this answer
I've spent so much time with this already, still can't get the value of NTE attribute. Can someone please help?
C#
StreamReader sr = new StreamReader(resp.GetResponseStream());
XPathDocument xmlDoc = new XPathDocument(sr); // holds xml document
XPathNavigator xmlNav = xmlDoc.CreateNavigator(); //evaluates XPath expressions
XPathNodeIterator node = xmlNav.Select("/DATA2SC/CALL");
string dne = xmlNav.GetAttribute("NTE", "");
Console.WriteLine(dne);
sr.Close();
XML
<?xml version="1.0"?>
<DATA2SC PIN="00000">
<CALL
TR_NUM="00000001"
STATUS="WAITING_FOR_APPROVAL"
NTE="$15.00">
<PROBLEM>
Text
</PROBLEM>
</CALL>
</DATA2SC>
Can you try this code please ?
I already check and it's work
StreamReader sr = new StreamReader("c:\\x.xml");
XPathDocument xmlDoc = new XPathDocument(sr); // holds xml document
XPathNavigator xmlNav = xmlDoc.CreateNavigator(); //evaluates XPath expressions
var node = xmlNav.SelectSingleNode("/DATA2SC/CALL");
string dne = node.GetAttribute("NTE", "");
Console.WriteLine(dne);
OR
XDocument docXmlWorld = XDocument.Load("c:\\x.xml");
foreach (var node1 in docXmlWorld.Descendants("DATA2SC"))
{
foreach (var node2 in node1.Descendants("CALL"))
{
string dne = node2.Attribute("NTE").Value;
Console.Out.WriteLine(dne);
}
}
Or you can do like this too:
XDocument docXmlWorld = XDocument.Load("c:\\x.xml");
//Get the first child => [DATA2SC]
XElement elementNodeDATA2SC = docXmlWorld.Element("DATA2SC");
//Get the first child => [CALL]
XElement elementNodeCALL = elementNodeDATA2SC.Element("CALL");
//Get the attribute NTE from [CALL] node
string dne = elementNodeCALL.Attribute("NTE").Value;
Console.Out.WriteLine(dne);
The Select method, returns a collection of all nodes with the specified XPath.
You can use SelectSingleNode, to select the first node.
var node = xmlNav.SelectSingleNode("/DATA2SC/CALL");
string dne = node.GetAttribute("NTE", "");
I'm attempting to display XML content tags from a word document that the user uploads, but I'm not sure how to explicitly pull out the data, and then display it as a string.
I think the below SHOULD find the correct descendent (or element, either should work right?) and then allow me to create a string out of it and display it, but I can't get the file to recognise xdoc). What I'm trying to return is located as "w.tag" in the bottom section of code.
Any ideas?
WordprocessingDocument _TempDoc = WordprocessingDocument.Open(Server.MapPath("~/") + filename, true);
//query to find particular descendants
var lv1s = from lv1 in xdoc.Descendants("table")
select new
{
Header = lv1.Attribute("name").Value,
Children = lv1.Descendants("tag")
};
//Loop through results
StringBuilder result = new StringBuilder();
foreach (var lv1 in lv1s)
{
result.AppendLine(lv1.Header);
foreach (var lv2 in lv1.Children)
result.AppendLine(" " + lv2.Attribute("name").Value);
}
//the label should contain the content controls of the document, using the class, XMLfromDocument
labelContentControls.Text = fileUpload_Displayx(XMLfromDocument.GetContentControls(_TempDoc));
public static XDocument GetXDocument(this OpenXmlPart part)
{
XDocument xdoc = part.Annotation<XDocument>();
if (xdoc != null)
return xdoc;
using (Stream str = part.GetStream())
using (StreamReader streamReader = new StreamReader(str))
using (XmlReader xr = XmlReader.Create(streamReader))
xdoc = XDocument.Load(xr);
part.AddAnnotation(xdoc);
return xdoc;
}
//following method gets the structure of the content controls / XML in the document
public static XElement GetContentControls( WordprocessingDocument document)
{
XElement contentControls = new XElement("ContentControls",
document
.MainDocumentPart
.GetXDocument()
.Root
.Element(W.body)
.Elements(W.sdt)
.Select(tableContentControl =>
new XElement("Table",
new XAttribute("Name", (string)tableContentControl
.Element(W.sdtPr).Element(W.tag).Attribute(
W.val)),
tableContentControl
.Descendants(W.sdt)
.Select(fieldContentControl =>
new XElement("Field",
new XAttribute("Name",
(string)fieldContentControl
.Element(W.sdtPr)
.Element(W.tag)
.Attribute(W.val)
)
)
)
)
)
);
// you cannot access the inner XML of the elemnt directly, you must concatenate to the child elements.
// string contentTitle = string.Concat(W.sdtPr);
//return W.tag;
return contentControls;
i have that xml file :
<?xml version="1.0" encoding="utf-8"?>
<reminders>
<reminder>
<Title>Alarm1</Title>
<Description>Desc1</Description>
<Time>03/07/2012 10:11AM</Time>
<snooze>1</snooze>
<repeat>None</repeat>
</reminder>
</reminders>
And i want to modify the innertext from Alarm1 to another value so i wrote that code which actually duplicate the entire node .
XmlDocument xml = new XmlDocument();
xml.Load("0.xml");
XmlNodeList elements = xml.SelectNodes("//reminders");
foreach (XmlNode element in elements)
{
if (element.InnerText == "Alarm1")
{
XmlNode newvalue = xml.CreateElement("MODIFIED");
element.ReplaceChild(newvalue, element);
xml.Save("0.xml");
}
}
And then tried another code :
foreach (XmlElement element in xml.SelectNodes("//reminder"))
{
if (element.InnerText == "Alarm1")
{
XmlNode newvalue = xml.CreateElement("MODIFIED");
element.ReplaceChild(newvalue, element);
xml.Save("0.xml");
}
}
But also doesn`t work....
EDIT 1 : [Figured out a new code]
XmlDocument xml = new XmlDocument();
xml.Load("0.xml");
foreach (XmlElement element in xml.SelectNodes("//reminder"))
{
foreach (XmlElement element1 in element)
{
if (element.SelectSingleNode("//Title").InnerText == "Alarm1")
{
XmlNode newvalue = xml.CreateElement("MODIFIED");
element.ReplaceChild(newvalue, element1);
xml.Save("0.xml");
}
}
}
But it made the Alarm1 becomes
<MODIFIED />
EDIT 2 : [I SOLVED IT :D]
Okay here is the code i could figure out :
XmlDocument xml = new XmlDocument();
xml.Load("0.xml");
foreach (XmlElement element in xml.SelectNodes("//reminder"))
{
foreach (XmlElement element1 in element)
{
if (element.SelectSingleNode("//Title").InnerText == "Alarm1")
{
MessageBox.Show(element1.InnerText);
XmlNode newvalue = xml.CreateElement("Title");
newvalue.InnerText = "MODIFIED";
element.ReplaceChild(newvalue, element1);
xml.Save("0.xml");
}
}
}
I`ll really appreciate your helps and thanks.
Try this:
xml.SelectSingleNode("//reminder/Title").InnerText = "NewValue";
Your foreach line is simply looping through a list of elements called "reminders", not it's child nodes.
Take a look at this xpath tutorial for more information:
http://www.w3schools.com/xpath/xpath_intro.asp
If you want to use linq with xml (I find it the best way) then you will want to use the System.Xml.Linq namespace. The classes in that namespace are all prefixed with just X not Xml. The functionality in this namespace is newer, better and much easier to manipulate with Linq.
var xml = XDocument.Load("0.xml");
var alarm1 = xml.Descendants("reminder")
.Single(r => r.Element("Title") == "Alarm1");
This code will give you a variable, alarm1 that is the reminder that has a title node of "Alarm1."
From that point its not clear to me exactly what you want to modify. If you just want to change the title then ...
alarm1.Element("Title").Value = "MODIFIED";
xml.Save("0.xml");
XDocument doc = XDocument.Load("0.xml");
IEnumerable<XElement> rech =
from el in doc.Root.Elements("reminder")
where (string)el.Element("Title") == "Alarm1"
select el;
if (rech.Count() != 0)
{
foreach (XElement el in rech)
{
el.Element("Title").SetValue("NEW TITLE");
}
}
doc.Save("0.xml");
XDocument xDoc = XDocument.Load(.....);
xDoc.Descendants("Title").First().Value = "New Value";
xDoc.Save(...)
XmlDocument xml = new XmlDocument();
xml.Load(...);
var newTitle = "MODIFIED";
var title_node = xml.GetElementsByTagName("Title");
if(!string.IsNullOrEmpty(newTitle) && title_node.Count > 0)
{
title_node[0].InnerText = newTitle;
}