I have a C# program which appends a specific inner block of XML element to an existing XML file (the XML document has 457 lines), the data is coming from a hard coded xls spreadsheet. I’m using LINQ to XML with xml.Descendants(), and AddBeforeSelf() method. The program works fine only if I have two records to append, and when I upload 3 records or more than two, the new XML Element are not inserted on the right section, the 3rd and succeeding records are appending on the wrong block of inner Element.
The goal of my program is to get the records from xls spreadsheet with multiple records and insert them to specific inner XML node. For example, xml element of OrderLine is currently on line 110, the program should insert the new block before line 110, it works when I only have two records, but it won’t work when I have more than two records.
Please see my XML Grid view, notice that there are 2 records under ReleaseLine, as well as under ShipUnit. The succeeding record must be inserted to the right node, or it must be nested under ReleaseLine and ShipUnit. I appreciate your help, thanks!
XML Grid View
Here is my code sample:
public class EditOrder
XElement xml = XElement.Load(#"C:\OrderTest.xml");
var childrens = xml.Descendants().ToArray();
var next_releaseline = childrens[110];
var next_shipunit = childrens[110];
Console.WriteLine(childrens[0]); //prints out the whole content
next_orderline.AddBeforeSelf(new XElement("ReleaseLine",
new XElement("ReleaseLineGid",
new XElement("PackagedItemRef",
new XElement("ItemQuantity",)
newXelement(Test),
....)));
next_shipunit.AddBeforeSelf(new XElement("ShipUnit",
new XElement("ShipUnit",
new XElement("WeightVolume",
new XElement("ShipUnitContent",))));
xml.Save(#"C:\OrderTest.xml");
And I'm calling the EditOrder class from another class to execute the program.
//Call from another class to execute the XML insertion
ProjectXML2.EditOrder.Main();
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication61
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<Transmission>" +
"<TransmissionBody>" +
"<Release>" +
"<ShiptoLocation></ShiptoLocation>" +
"<TimeFrame></TimeFrame>" +
"</Release>" +
"</TransmissionBody>" +
"</Transmission>";
XDocument doc = XDocument.Parse(xml);
XElement releaseLine = new XElement("ReleaseLine",
new XElement("ReleaseLineGid",
new XElement("PackagedItemRef",
new XElement("ItemQuantity"))));
XElement shipUnit = new XElement("ShipUnit",
new XElement("ShipUnit",
new XElement("WeightVolume",
new XElement("ShipUnitContent"))));
XElement release = doc.Descendants("Release").FirstOrDefault();
release.Add(releaseLine);
release.Add(shipUnit);
}
}
}
Related
//I would like to get some advice on how to replace the node value of a element in XML using XMLElement or XElement. Right now I'm trying it with XMLElement but the compiler gives me only errors or the XML replace the whole document insted of selected node. I have a XML/XSD sample that I need to fill with data and create like 500 XML a day but I cant insert the data into the right elements.
I expect that I can replace / insert data into the element value. Like I want to change the City text value. I have a table with content and based on the filled data I need to add it into specific XML elements and save.
As for now every sample code I found and some I covered up did give compile errors / null reference object( at assigning InnerText) or deleted all the elements and added just one line with my text value.
Te code below gives me ''Object reference not set to an instance of an object.'
' at .InnerText. Actually the declatarion of SelectSingleNode("ID") return me also a null.
-<Receiver>
-<ID>
<RNumber>9999999999</RNumber>
<Name>ABC AGD sp. z o. o.</Name>
</ID>
-<Address>
-<AddressSpec>
<Country>PL</Country>
<Street>Kwiatowa</Street>
<HouseNum>1</HouseNum>
<City>Warszawa</City>
</AddressSpec>
</Address>
<Email>test#test.pl</Email>
<Phone>667444555</Phone>
</ID>
-</Receiver>
using System.IO;
using System.Net;
using System.Xml;
using System.Xml.Linq;
class ReplaceXMLData
{
public static void Main(Args _args)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(//path/sample.xml);
System.Xml.XmlElement root = doc.DocumentElement;
System.Xml.XmlElement Street = doc.SelectSingleNode("Street");
Street.innertext = "random";
doc.AppendChild(root);
doc.Save(path);
info("Details added Successfully");
}}
Here is solution using Xml Linq (XDocument). You need to use Decendants with FirstOrDefault
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication15
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement id = doc.Descendants("ID").FirstOrDefault();
}
}
}
I have an XML file, similar to the one below. I will read the XML and find the EmpId node. Based on other data will determine if I need to keep this node or delete the node. Currently have a process which reads it and creates a list of Record Ids which I will need to remove. In the below XML I will want to keep the EmpId = Emp1 and remove EmpId = Emp2. The removal is the node.
I believe the best approach is to read the XML first to determine which nodes to keep and then go thru the XML again and remove the necessary nodes.
What's the best approach to remove these nodes?
I'm open to creating a new XML document and creating the node which need to be kept. Based on the data I'm reading it's 50/50 if I'll be removing more nodes or keeping more nodes.
...
<HeaderNode>
<Details>
<SubmissionId>1</SubmissionId>
<EmpDetail>
<RecordId>1</RecordId>
<EmpDempgraphic>
<EmpId>Emp1</EmpId>
</EmpDempgraphic>
</EmpDetail>
<EmpDetail>
<RecordId>2</RecordId>
<EmpDempgraphic>
<EmpId>Emp2</EmpId>
</EmpDempgraphic>
</EmpDetail>
</Details>
</HeaderNode>
...
Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace XML
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement EmpDetail = doc.Descendants("EmpDetail").Where(x => (int)x.Element("RecordId") == 2).FirstOrDefault();
EmpDetail.Remove();
}
}
}
Unless the document is huge, just load it into an XDocument, Remove the elements you don't want, and Save the document back out.
How do I read and parse an XML file in C#?
XmlDocument to read an XML from string or from file.
using System.Xml;
XmlDocument doc = new XmlDocument();
doc.Load("c:\\temp.xml");
or
doc.LoadXml("<xml>something</xml>");
then find a node below it ie like this
XmlNode node = doc.DocumentElement.SelectSingleNode("/book/title");
or
foreach(XmlNode node in doc.DocumentElement.ChildNodes){
string text = node.InnerText; //or loop through its children as well
}
then read the text inside that node like this
string text = node.InnerText;
or read an attribute
string attr = node.Attributes["theattributename"]?.InnerText
Always check for null on Attributes["something"] since it will be null if the attribute does not exist.
LINQ to XML Example:
// Loading from a file, you can also load from a stream
var xml = XDocument.Load(#"C:\contacts.xml");
// Query the data and write out a subset of contacts
var query = from c in xml.Root.Descendants("contact")
where (int)c.Attribute("id") < 4
select c.Element("firstName").Value + " " +
c.Element("lastName").Value;
foreach (string name in query)
{
Console.WriteLine("Contact's Full Name: {0}", name);
}
Reference: LINQ to XML at MSDN
Here's an application I wrote for reading xml sitemaps:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
namespace SiteMapReader
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
// thereare a couple child nodes here so only take data from node named loc
if (locNode.Name == "loc")
{
// get the content of the loc node
string loc = locNode.InnerText;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
}
catch { }
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
static void readSitemap()
{
}
}
}
Code on Paste Bin
http://pastebin.com/yK7cSNeY
There are lots of way, some:
XmlSerializer. use a class with the target schema
you want to read - use XmlSerializer
to get the data in an Xml loaded into
an instance of the class.
Linq 2 xml
XmlTextReader.
XmlDocument
XPathDocument (read-only access)
You could use a DataSet to read XML strings.
var xmlString = File.ReadAllText(FILE_PATH);
var stringReader = new StringReader(xmlString);
var dsSet = new DataSet();
dsSet.ReadXml(stringReader);
Posting this for the sake of information.
You can either:
Use XmlSerializer class
Use XmlDocument class
Examples are on the msdn pages provided
Linq to XML.
Also, VB.NET has much better xml parsing support via the compiler than C#. If you have the option and the desire, check it out.
Check out XmlTextReader class for instance.
There are different ways, depending on where you want to get.
XmlDocument is lighter than XDocument, but if you wish to verify minimalistically that a string contains XML, then regular expression is possibly the fastest and lightest choice you can make. For example, I have implemented Smoke Tests with SpecFlow for my API and I wish to test if one of the results in any valid XML - then I would use a regular expression. But if I need to extract values from this XML, then I would parse it with XDocument to do it faster and with less code. Or I would use XmlDocument if I have to work with a big XML (and sometimes I work with XML's that are around 1M lines, even more); then I could even read it line by line. Why? Try opening more than 800MB in private bytes in Visual Studio; even on production you should not have objects bigger than 2GB. You can with a twerk, but you should not. If you would have to parse a document, which contains A LOT of lines, then this documents would probably be CSV.
I have written this comment, because I see a lof of examples with XDocument. XDocument is not good for big documents, or when you only want to verify if there the content is XML valid. If you wish to check if the XML itself makes sense, then you need Schema.
I also downvoted the suggested answer, because I believe it needs the above information inside itself. Imagine I need to verify if 200M of XML, 10 times an hour, is valid XML. XDocument will waste a lof of resources.
prasanna venkatesh also states you could try filling the string to a dataset, it will indicate valid XML as well.
public void ReadXmlFile()
{
string path = HttpContext.Current.Server.MapPath("~/App_Data"); // Finds the location of App_Data on server.
XmlTextReader reader = new XmlTextReader(System.IO.Path.Combine(path, "XMLFile7.xml")); //Combines the location of App_Data and the file name
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
break;
case XmlNodeType.Text:
columnNames.Add(reader.Value);
break;
case XmlNodeType.EndElement:
break;
}
}
}
You can avoid the first statement and just specify the path name in constructor of XmlTextReader.
If you want to retrive a particular value from an XML file
XmlDocument _LocalInfo_Xml = new XmlDocument();
_LocalInfo_Xml.Load(fileName);
XmlElement _XmlElement;
_XmlElement = _LocalInfo_Xml.GetElementsByTagName("UserId")[0] as XmlElement;
string Value = _XmlElement.InnerText;
Here is another approach using Cinchoo ETL - an open source library to parse xml file with few lines of code.
using (var r = ChoXmlReader<Item>.LoadText(xml)
.WithXPath("//item")
)
{
foreach (var rec in r)
rec.Print();
}
public class Item
{
public string Name { get; set; }
public string ProtectionLevel { get; set; }
public string Description { get; set; }
}
Sample fiddle: https://dotnetfiddle.net/otYq5j
Disclaimer: I'm author of this library.
How do I read and parse an XML file in C#?
XmlDocument to read an XML from string or from file.
using System.Xml;
XmlDocument doc = new XmlDocument();
doc.Load("c:\\temp.xml");
or
doc.LoadXml("<xml>something</xml>");
then find a node below it ie like this
XmlNode node = doc.DocumentElement.SelectSingleNode("/book/title");
or
foreach(XmlNode node in doc.DocumentElement.ChildNodes){
string text = node.InnerText; //or loop through its children as well
}
then read the text inside that node like this
string text = node.InnerText;
or read an attribute
string attr = node.Attributes["theattributename"]?.InnerText
Always check for null on Attributes["something"] since it will be null if the attribute does not exist.
LINQ to XML Example:
// Loading from a file, you can also load from a stream
var xml = XDocument.Load(#"C:\contacts.xml");
// Query the data and write out a subset of contacts
var query = from c in xml.Root.Descendants("contact")
where (int)c.Attribute("id") < 4
select c.Element("firstName").Value + " " +
c.Element("lastName").Value;
foreach (string name in query)
{
Console.WriteLine("Contact's Full Name: {0}", name);
}
Reference: LINQ to XML at MSDN
Here's an application I wrote for reading xml sitemaps:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
namespace SiteMapReader
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
// thereare a couple child nodes here so only take data from node named loc
if (locNode.Name == "loc")
{
// get the content of the loc node
string loc = locNode.InnerText;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
}
catch { }
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
static void readSitemap()
{
}
}
}
Code on Paste Bin
http://pastebin.com/yK7cSNeY
There are lots of way, some:
XmlSerializer. use a class with the target schema
you want to read - use XmlSerializer
to get the data in an Xml loaded into
an instance of the class.
Linq 2 xml
XmlTextReader.
XmlDocument
XPathDocument (read-only access)
You could use a DataSet to read XML strings.
var xmlString = File.ReadAllText(FILE_PATH);
var stringReader = new StringReader(xmlString);
var dsSet = new DataSet();
dsSet.ReadXml(stringReader);
Posting this for the sake of information.
You can either:
Use XmlSerializer class
Use XmlDocument class
Examples are on the msdn pages provided
Linq to XML.
Also, VB.NET has much better xml parsing support via the compiler than C#. If you have the option and the desire, check it out.
Check out XmlTextReader class for instance.
There are different ways, depending on where you want to get.
XmlDocument is lighter than XDocument, but if you wish to verify minimalistically that a string contains XML, then regular expression is possibly the fastest and lightest choice you can make. For example, I have implemented Smoke Tests with SpecFlow for my API and I wish to test if one of the results in any valid XML - then I would use a regular expression. But if I need to extract values from this XML, then I would parse it with XDocument to do it faster and with less code. Or I would use XmlDocument if I have to work with a big XML (and sometimes I work with XML's that are around 1M lines, even more); then I could even read it line by line. Why? Try opening more than 800MB in private bytes in Visual Studio; even on production you should not have objects bigger than 2GB. You can with a twerk, but you should not. If you would have to parse a document, which contains A LOT of lines, then this documents would probably be CSV.
I have written this comment, because I see a lof of examples with XDocument. XDocument is not good for big documents, or when you only want to verify if there the content is XML valid. If you wish to check if the XML itself makes sense, then you need Schema.
I also downvoted the suggested answer, because I believe it needs the above information inside itself. Imagine I need to verify if 200M of XML, 10 times an hour, is valid XML. XDocument will waste a lof of resources.
prasanna venkatesh also states you could try filling the string to a dataset, it will indicate valid XML as well.
public void ReadXmlFile()
{
string path = HttpContext.Current.Server.MapPath("~/App_Data"); // Finds the location of App_Data on server.
XmlTextReader reader = new XmlTextReader(System.IO.Path.Combine(path, "XMLFile7.xml")); //Combines the location of App_Data and the file name
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
break;
case XmlNodeType.Text:
columnNames.Add(reader.Value);
break;
case XmlNodeType.EndElement:
break;
}
}
}
You can avoid the first statement and just specify the path name in constructor of XmlTextReader.
If you want to retrive a particular value from an XML file
XmlDocument _LocalInfo_Xml = new XmlDocument();
_LocalInfo_Xml.Load(fileName);
XmlElement _XmlElement;
_XmlElement = _LocalInfo_Xml.GetElementsByTagName("UserId")[0] as XmlElement;
string Value = _XmlElement.InnerText;
Here is another approach using Cinchoo ETL - an open source library to parse xml file with few lines of code.
using (var r = ChoXmlReader<Item>.LoadText(xml)
.WithXPath("//item")
)
{
foreach (var rec in r)
rec.Print();
}
public class Item
{
public string Name { get; set; }
public string ProtectionLevel { get; set; }
public string Description { get; set; }
}
Sample fiddle: https://dotnetfiddle.net/otYq5j
Disclaimer: I'm author of this library.
I want to add an XML string as new node to an existing XML document.
For example, suppose the input from the user is:
<bk:book>
<title>Pride And Prejudice</title>
<authorlastname>Jane</authorlastname>
<authorfirstname>Austen</authorfirstname>
<price>24.95</price>
</bk:book>
I am trying to insert that user input as follows:
xml_SourceDoc.Root.LastNode.AddAfterSelf(XElement.Parse(xmlString));
However, that statement is raising this exception:
bk is an undeclared prefix. Line 1, position 2.
How can I change my approach so that I can successfully insert any text that is input by the user?
If you really don't know what the user will enter, you can simply handle it as CDATA via the LINQ to XML XCData Class.
Here is what your sample data would look like when inserted as a node in a container XML document:
<doc>
<content><![CDATA[<bk:book>
<title>Pride And Prejudice</title>
<authorlastname>Jane</authorlastname>
<authorfirstname>Austen</authorfirstname>
<price>24.95</price>
</bk:book>]]></content>
</doc>
And here is an example program that creates the above example document:
using System;
using System.Xml;
using System.Xml.Linq;
public class CDataExample
{
public static void Main()
{
string documentXml = "<doc><content></content></doc>";
XElement doc = XElement.Parse(documentXml, LoadOptions.None);
string userInput =
#"<bk:book>
<title>Pride And Prejudice</title>
<authorlastname>Jane</authorlastname>
<authorfirstname>Austen</authorfirstname>
<price>24.95</price>
</bk:book>";
XCData cdata = new XCData(userInput);
doc.Element("content").Add(cdata);
Console.WriteLine(doc.ToString());
}
}
Check the xml is parsable first:
Check well-formed XML without a try/catch?
if(IsValidXML(xmlString))
{
xml_SourceDoc.Root.LastNode.AddAfterSelf(XElement.Parse(xmlString));
}
first, create XElement you want to add:
xmlString = new XElement(new XElement("book", new XAttribute("bk) ,(new XElement("title",
titleValue), new EXelement("authorlastname", authorlastNameValue ...
and so on.
then add it:
xml_SourceDoc.Root.Add(xmlString);
exception you mention is caused that you do not add attribute creating XElement