XML example :
<?xml version="1.0" encoding="utf-8" ?>
<brand name="brand1" num_brand="118" enabled="True">
<price>
<nodePattern>pattern</nodePattern>
<attribute type="text" ></attribute>
<treatment enabled="1" type="Regex">reg</treatment>
</price>
<title>
<nodePattern>pattern</nodePattern>
<attribute type="text" ></attribute>
<treatment enabled="1" type="Regex">reg</treatment>
</title>
</brand>
Please, how can I get the different attributes values and text for all my different nodes (for example name, num_brand and enabled for brand, enabled, type and "reg" for treatment) using System.Xml.Linq ?
Thank you !
The System.Xml.Linq namespace is much nicer than the System.Xml namespace. Your XDocument has one XElement, which in turn has children elements. Each element has attributes and a value.
Here's an example for you:
var text = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<brand name=""brand1"" num_brand=""118"" enabled=""True"">
<price>
<nodePattern>pattern</nodePattern>
<attribute type=""text"" ></attribute>
<treatment enabled=""1"" type=""Regex"">reg</treatment>
</price>
<title>
<nodePattern>pattern</nodePattern>
<attribute type=""text"" ></attribute>
<treatment enabled=""1"" type=""Regex"">reg</treatment>
</title>
</brand>";
XDocument document = XDocument.Parse(text);
// one root element - "brand"
System.Diagnostics.Debug.Assert(document.Elements().Count() == 1);
XElement brand = document.Element("brand");
// brand has two children - price and title
foreach (var element in brand.Elements())
Console.WriteLine("element name: " + element.Name);
// brand has three attributes
foreach (var attr in brand.Attributes())
Console.WriteLine("attribute name: " + attr.Name + ", value: " + attr.Value);
You have many ways to do that. One of them is the XmlDocument.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXML);
foreach(XmlNode node in xmlDoc.DocumentElement.ChildNodes){
string text = node.InnerText; //you can loop through children
}
Take a look on this post :
How do I read and parse an XML file in C#?
Personnaly I like the Linq To Xml approach, more infos here :
https://msdn.microsoft.com/en-us/library/bb387061.aspx
try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENMAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENMAME);
var brand = doc.Descendants("brand").Select(x => new
{
name = x.Attribute("name").Value,
num_brand = x.Attribute("num_brand").Value,
enabled = x.Attribute("enabled").Value,
nodePattern = x.Element("price").Element("nodePattern").Value,
attribute = x.Element("price").Element("attribute").Attribute("type").Value,
priceTreatmentEnable = x.Element("price").Element("treatment").Attribute("enabled").Value,
priceTreatmentType = x.Element("price").Element("treatment").Attribute("type").Value,
priceTreatment = x.Element("price").Element("treatment").Value,
titleTreatmentEnable = x.Element("title").Element("treatment").Attribute("enabled").Value,
titleTreatmentType = x.Element("title").Element("treatment").Attribute("type").Value,
titleTreatment = x.Element("title").Element("treatment").Value
}).FirstOrDefault();
}
}
}
Related
I have two XMLs'
XML1:
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>3421</Value>
<Value>John</Value>
</Object>
</Sequence>
</Data>'
XML2:
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>1234</Value>
<Value>SAM</Value>
</Object>
</Sequence>
</Data>'
I want the output like below
'<?xml version="1.0" encoding="utf-8"?>
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<Object>
<Value>3421</Value>
<Value>John</Value>
</Object>
<Object>
<Value>1234</Value>
<Value>SAM</Value>
</Object>
</Sequence>
</Data>'
I.e I want to merge Object tag from XML2 to XML1 using C# code.
Could someone please help me?
You can use XPath to select the nodes you need and then simply use .NET xml using System.Xml
For more information look at https://www.w3schools.com/xml/xpath_intro.asp
Load Xml Documents
I saved the two sample xml-files you provided to separate files and imported them like this
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
using (var sw = new StreamReader("xml1.xml"))
{
var text = sw.ReadToEnd();
doc1.LoadXml(text);
}
using (var sw = new StreamReader("xml2.xml"))
{
var text = sw.ReadToEnd();
doc2.LoadXml(text);
}
Select nodes with XPATH
We will take all elements that have the name 'object' and add them to child of the other xml's 'sequence'-element. Therefore we select the 'sequence'-element of one document and the 'object'elements of the other document.
var sequenceNodes = doc1.SelectSingleNode("/Data/Sequence");
var objectNodes = doc2.SelectNodes("/Data/Sequence/Object");
Concat the nodes into one document
Then we take each 'object'-element, import it into the other document-context and append it under the 'sequence'-node
foreach (XmlNode node in objectNodes)
{
XmlNode importedNode = doc1.ImportNode(node, true);
sequenceNodes.AppendChild(importedNode);
}
Output the file
using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
doc1.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();
File.AppendAllText("out.xml", stringWriter.GetStringBuilder().ToString());
}
The outputfile looks like this:
Please, take a look at this solution:
what-is-the-fastest-way-to-combine-two-xml-files-into-one
Using Union method seems to be what you are seeking.
Run an XSLT transformation:
<xsl:template name="merge">
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Filename>1234</Filename>
<Sequence Type="FRONT">
<xsl:copy-of select="document('a.xml')//Object"/>
<xsl:copy-of select="document('b.xml')//Object"/>
</Sequence>
</Data>
</xsl:template>
Try following linq which joins all filenames with same value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication166
{
class Program
{
const string FILENAME1 = #"c:\temp\test.xml";
const string FILENAME2 = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XDocument doc1 = XDocument.Load(FILENAME1);
XNamespace ns1 = doc1.Root.GetDefaultNamespace();
XDocument doc2 = XDocument.Load(FILENAME2);
XNamespace ns2 = doc2.Root.GetDefaultNamespace();
var joins= from d1 in doc1.Descendants(ns1 + "Data")
join d2 in doc2.Descendants(ns2 + "Data")
on (string)d1.Element(ns1 + "Filename") equals (string)d2.Element(ns2 + "Filename")
select new { d1 = d1, d2 = d2};
foreach (var join in joins)
{
XElement d2Object = join.d2.Descendants("Object").FirstOrDefault();
join.d1.Descendants("Sequence").FirstOrDefault().Add(XElement.Parse(d2Object.ToString()));
}
}
}
}
I have an xml file with the following format that I'm trying to parse with C# Linq to XML. The problem is that it has this separate metadata element which is the only thing identifying the values below. Is there a good way to do this? I don't have any power to change the format of this file.
<?xml version="1.0" encoding="utf-8?>
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<metadata>
<item name="Address Line 1" type="xs:string" length="512"/>
<item name="Address Line 2" type="xs:string" length="512"/>
<item name="Date Of Birth" type="xs:dateTime"/>
</metadata>
<data>
<row>
<value>123 Main St</value>
<value xs:nil="true" />
<value>1970-01-01T00:00:00</value>
</row>
<row>
<value>125 Main St</value>
<value>Apt 1</value>
<value>1980-01-01T00:00:00</value>
</row>
</data>
</dataset>
The actual file has about 30 item and corresponding value elements in each row and several hundred row elements following this format. I'm basically looking for the best way to match up the metadata to the values. If Linq to XML is not the best way to achieve this, I'm open to other suggestions that work with C# and .NET 4.5.
I tried just collecting the metadata items in a list and using indices to match them to the values, but it seems to build the list in an arbitrary order, so I'm not sure I can rely on that ordering to identify the values.
XDocument xdoc = XDocument.Load(#"Export.xml");
XNamespace xns = "http://developer.cognos.com/schemas/xmldata/1/";
var metadataQuery = from t in xdoc.Descendants(xns + "item") select t;
List<XElement> metadata = metadataQuery.ToList(); // This list appears to be ordered randomly
The order of elements is always the same, in the order in which they appear in the xml file.
var xdoc = XDocument.Load(#"Export.xml");
XNamespace xns = "http://developer.cognos.com/schemas/xmldata/1/";
var metadata = xdoc.Descendants(xns + "item").ToList();
var data = xdoc.Descendants(xns + "row").ToList();
foreach (var datum in data)
{
var values = datum.Elements(xns + "value").ToList();
for (int i = 0; i < values.Count; i++)
{
Console.WriteLine(metadata[i].Attribute("name").Value + ": " + values[i].Value);
}
Console.WriteLine();
}
Here is a simple method to parse file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement dataset = (XElement)doc.FirstNode;
XNamespace ns = dataset.Name.Namespace;
var results = doc.Descendants(ns + "row").Select(x => new {
firstAddr = (string)x.Elements(ns + "value").FirstOrDefault(),
secondAddr = (string)x.Elements(ns + "value").Skip(1).FirstOrDefault(),
dob = (DateTime)x.Elements(ns + "value").Skip(2).FirstOrDefault()
}).ToList();
}
}
}
i wish you a happy new year!
I got following XML structure:
<?xml version="1.0" encoding="UTF-8"?>
<SW.CodeBlock ID="0">
<SW.CompileUnit ID="1">
<AttributeList>
<NetworkSource>
<FlgNet xmlns="http://www.TEST.com">
<Parts> </Parts>
</FlgNet>
</NetworkSource>
</AttributeList>
</SW.CompileUnit>
<SW.CompileUnit ID="2">
<AttributeList>
<NetworkSource>
<FlgNet xmlns="http://www.TEST.COM">
<Parts> </Parts>
</FlgNet>
</NetworkSource>
</AttributeList>
</SW.CompileUnit>
</SW.CodeBlock>
How can i add a Child in "Parts" from SW.CompileUnit ID = 1 and SW.CompileUnit ID = 2 etc.?
I would like to create a loop (for-loop), which creates a child in "Parts" for every "SW.CompileUnit"-Node
Could you please help me?
PS: I use VS2015, C#, not using Linq or XPath etc.
Until now I add a child like this:
XmlNode xPiece = xdoc.SelectSingleNode("//NS2:Parts",nsmgr);
xPiece.AppendChild(myXMLElement);
but it only adds a child in the first SW.CompileUnit Node (with the ID=1)
...
Thanks in advance
SelectSingleNode() returns only the first matched elements. To get all matched elements, you're supposed to use SelectNodes() instead :
var nodes = xdoc.SelectNodes("//NS2:Parts",nsmgr);
foreach(XmlNode node in nodes)
{
//create new myXMLElement
....
//and then append it to current <Parts>
node.AppendChild(myXMLElement);
}
Btw, parameter of SelectNodes() and SelectSingleNode() are XPath expressions (just saying, because you wrote "I use VS2015, C#, not using Linq or XPath etc").
Use XML Linq. Not sure if I got your request exactly correct.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<SW.CodeBlock ID=\"0\">" +
"<SW.CompileUnit ID=\"1\">" +
"<AttributeList>" +
"<NetworkSource>" +
"<FlgNet xmlns=\"http://www.TEST.com\">" +
"<Parts> </Parts>" +
"</FlgNet>" +
"</NetworkSource>" +
"</AttributeList>" +
"</SW.CompileUnit>" +
"<SW.CompileUnit ID=\"2\">" +
"<AttributeList>" +
"<NetworkSource>" +
"<FlgNet xmlns=\"http://www.TEST.COM\">" +
"<Parts> </Parts>" +
"</FlgNet>" +
"</NetworkSource>" +
"</AttributeList>" +
"</SW.CompileUnit>" +
"</SW.CodeBlock>";
XDocument doc = XDocument.Parse(xml);
var compileUnits = doc.Descendants("SW.CompileUnit").Select(x => new {
ID = (string)x.Attribute("ID"),
parts = x.Descendants().Where(y => y.Name.LocalName == "Parts").FirstOrDefault()
}).ToList();
foreach (var compileUnit in compileUnits)
{
compileUnit.parts.Add(new XElement(compileUnit.parts.Name.Namespace + "ID", compileUnit.ID));
}
}
}
}
I have a problem, I can't get...and don't know what is wrong with it?
the code:
static void Main(string[] args)
{
XDocument doc = XDocument.Load(args[0] + "/?verb=GetRecord&metadataPrefix=p3dm&identifier=" + 1);
doc.Save("doc.xml");
var node = doc.Descendants("identifier");
foreach (var n in node)
{
doc.Save("file_" + n.Value + ".xml");
}
}
the doc.xml looks like:
<?xml version="1.0" encoding="utf-8"?>
<OAI-PMH xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
<responseDate>...</responseDate>
<request verb="GetRecord" identifier="1"</request>
<GetRecord>
<record>
<header>
<identifier>1</identifier>
<datestamp>...</datestamp>
</header>
<metadata>
<P3DM xmlns="..." xsi:schemaLocation="...">
<MODELINFOID>1</MODELINFOID>
<TITLE>Roth</TITLE>
....
As we can see in doc.xml, there is element with the value 1, and using Descendants and foreach...I would like to save same document but using tag value to name my output file. What am I doing wrong. This code is just for testing.
Your XML has default namespace. You can use XNamespace plus element's local name to address an element in namespace :
......
//assume that the default namespace declared as <OAI-PMH xmlns="dummy.uri" ....>
XNamespace d = "dummy.uri";
var node = doc.Descendants(d+"identifier");
......
Is there a way to get the innertext of a node when the node is inside a collection
Currently i have this
Collection<string> DependentNodes = new Collection<string>();
foreach (XmlNode node in nodes)
{
for (int i = 0; i < node.ChildNodes.Count; i++)
{
DependentNodes.Add(node.ChildNodes[i].InnerXml);
//the reason i'm using InnerXml is that it will return all the child node of testfixture in one single line,then we can find the category & check if there's dependson
}
}
string selectedtestcase = "abc_somewords";
foreach (string s in DependentNodes)
{
if(s.Contains(selectedtestcase))
{
MessageBox.Show("aaa");
}
}
When i debug string s or the index has this inside of it[in a single line]
<testfixture name="1" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="a" description="a">
<dependencies>
<dependson typename="dependsonthis" />
</dependencies>
</test>
</testfixture>
What i'm trying to do is when we reach "testfixture 1" it will find "abc_somewords" & search the "dependson typename"node(if any) and get the "typename"(which is "dependonthis").
Could you use linq to xml. Something like the below might be a decent start
xml.Elements("categories").Where(x => x.Element("category").Value.Contains(selectedtestcase));
This is off the top of my head so might will need refining
P.S. Use XElement.Load or XElement.Parse to get your xml into XElements
Since you already working with XmlNode you could use a XPath expression to select the desired textfixture node, and select the dependency value:
XmlDocument doc = // ...
XmlNode node = doc.SelectSingleNode("//testfixture[contains(categories/category, \"abc\")]/test/dependencies/dependson/");
if (node != null)
{
MessageBox.Show(node.Attributes["typename"]);
}
This selects the dependson node which belongs to a testfixture node with a category containing "abc". node.Attributes["typename"] will return the value of the typename attribute.
Edited:
Updated XPath expression to the more specific question information
Assumptions
As you are looping in your code and wanting to create a collection I'm assuming the actual Xml File has several testfixture nodes inside such as the below assumed example:
<root>
<testfixture name="1" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="a" description="a">
<dependencies>
<dependson typename="dependsonthis" />
</dependencies>
</test>
</testfixture>
<testfixture name="2" description="a">
<categories>
<category>another_value</category>
</categories>
<test name="b" description="a">
<dependencies>
<dependson typename="secondentry" />
</dependencies>
</test>
</testfixture>
<testfixture name="3" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="c" description="a">
<dependencies>
<dependson typename="thirdentry" />
</dependencies>
</test>
</testfixture>
</root>
The Code using Linq to Xml
To use Linq you must reference the following name spaces:
using System.Linq;
using System.Xml.Linq;
Using Linq To Xml on the above assumed xml file structure would look like this:
// To Load Xml Content from File.
XDocument doc1 = XDocument.Load(#"C:\MyXml.xml");
Collection<string> DependentNodes = new Collection<string>();
var results =
doc1.Root.Elements("testfixture")
.Where(x => x.Element("categories").Element("category").Value.Contains("abc_somewords"))
.Elements("test").Elements("dependencies").Elements("dependson").Attributes("typename").ToArray();
foreach (XAttribute attribute in results)
{
DependentNodes.Add(attribute.Value.Trim());
}
Result
The resulting Collection will contain the following:
As you can see, only the text of the typename attribute has been extracted where the dependson nodes where in a testfixture node which contained a category node with the value of abc_somewords.
Additional Notes
If you read the xml from a string you can also use this:
// To Load Xml Content from a string.
XDocument doc = XDocument.Parse(myXml);
If your complete Xml structure is different, feel free to post it and I change the code to match.
Have Fun.
I don't know what is "nodes" you are using.
Here is code with your requirement(What I understood).
Collection<XmlNode> DependentNodes = new Collection<XmlNode>();
XmlDocument xDoc = new XmlDocument();
xDoc.Load(#"Path_Of_Your_xml");
foreach (XmlNode node in xDoc.SelectNodes("testfixture")) // Here I am accessing only root node. Give Xpath if ur requrement is changed
{
for (int i = 0; i < node.ChildNodes.Count; i++)
{
DependentNodes.Add(node.ChildNodes[i]);
}
}
string selectedtestcase = "abc_somewords";
foreach (var s in DependentNodes)
{
if (s.InnerText.Contains(selectedtestcase))
{
Console.Write("aaa");
}
}
using System;
using System.Xml;
namespace ConsoleApplication6
{
class Program
{
private const string XML = "<testfixture name=\"1\" description=\"a\">" +
"<categories>" +
"<category>abc_somewords</category>" +
"</categories>" +
"<test name=\"a\" description=\"a\">" +
"<dependencies>" +
"<dependson typename=\"dependsonthis\" />" +
"</dependencies>" +
"</test>" +
"</testfixture>";
static void Main(string[] args)
{
var document = new XmlDocument();
document.LoadXml(XML);
var testfixture = document.SelectSingleNode("//testfixture[#name = 1]");
var category = testfixture.SelectSingleNode(".//category[contains(text(), 'abc_somewords')]");
if(category != null)
{
var depends = testfixture.SelectSingleNode("//dependson");
Console.Out.WriteLine(depends.Attributes["typename"].Value);
}
Console.ReadKey();
}
}
}
Output: dependsonthis