C# xmlReader simple XML - c#

I've got simple XML like:
<?xml version="1.0" encoding="utf-8"?>
<ftp>
<address>ftp://ftp.example.com/</address>
<user>jondoe</user>
<password>Password123</password>
</ftp>
And I wanted to use this C# code:
using (XmlReader reader = XmlReader.Create(xmlPath))
{
reader.ReadToFollowing("address");
string ftpAddress = reader.ReadElementContentAsString();
reader.ReadToFollowing("user");
string ftpUser = reader.ReadElementContentAsString();
reader.ReadToFollowing("password");
string ftpPw = reader.ReadElementContentAsString();
}
It's working fine when I want to read only one content, like address but it fails when I want to read more/next ones. I thought this would be logical but it seems like it's not. How to read that XML and all elements of it: address, user and password? I would like to use XmlReader for that.

The simplest form I can think of would be this one:
XElement xml = XElement.Parse(File.ReadAllText(xmlPath));
var ftpAddress = xml.Element("address").Value;
var ftpUser = xml.Element("user").Value;
var ftpPwd = xml.Element("user").Value;
Of course you should add some safety by checking for null values and if the file exists..
Update:
I would implement a failsafe version this way:
if (!File.Exists(xmlPath))
throw new FileNotFoundException(string.Format("The FTP configuration file {0} is missing", xmlPath));
XElement xml = XElement.Parse(File.ReadAllText(xmlPath));
var ftpAddress = GetConfigValue(xml, "address");
var ftpUser = GetConfigValue(xml, "user");
var ftpPwd = GetConfigValue(xml, "password");
With GetConfigValue like this:
private static string GetConfigValue(XContainer parent, XName name)
{
var element = parent.Element(name);
if (element == null)
throw new ArgumentException(string.Format("Invalid configuration file, missing element {0}", name));
return element.Value;
}

Related

Remove Node from XmlSerializer

I want to save a WCF request as file to a catalog, but when the file(xml) is created from the XmlSerializer it adds a Node to the XML file which I do not want. Called ("ProjectInRequest"). I do not want to have this node in the created output file.
How can I remove it in the most sufficent way?
I have tried to load the whole document in XmlDocument where the file should be created, but the creation of the file does not happen until the response is returned. There fore the whole service fails.
namespace SymbrioProjectIn
{
public partial class SymbrioProjectIn_v1
{
public ProjectInResponse GetSymbrioProjectInRequest(ProjectInRequest request)
{
if (request == null || request.SymbrioXMLProjectIn.Project == null && request.SymbrioXMLProjectIn.Routing == null )
{
throw new ApplicationException("Request is null: You need to send info in request.");
}
else
{
try
{
//All we want to do is take the Request that SAP sends and store it to a file catalog.
//We can after that use BizTalk to move the file too another sftp catalog for Symbrio
string path = AppDomain.CurrentDomain.BaseDirectory;
String dir = Path.GetDirectoryName(path);
dir += "\\ProjectIn_Data";
string filename = dir + "\\ProjectIn.xml";
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir); // inside the if statement
//XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
//Add an empty namespace and empty value
//ns.(string.Empty, string.Empty);
//XElement ProjectInRequest = new XElement("ProjectInRequest");
//ProjectInRequest.RemoveAll();
/*XmlAttributes ignore = new XmlAttributes() { XmlIgnore = true };
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(ProjectInRequest), "ProjectInRequest", ignore);*/
XmlSerializer ser = new XmlSerializer(request.GetType(), new XmlRootAttribute("ProjectInRequest").ElementName); //Tried som code from internet not sure what it does
FileStream fs = File.Open(
filename,
FileMode.OpenOrCreate,
FileAccess.Write,
FileShare.ReadWrite);
ser.Serialize(fs, request);
var response = new SymbrioProjectIn.ProjectInResponse{
SymbrioXMLProjectIn = request.SymbrioXMLProjectIn
};
//Looks like xmlSerialiser creates some type of namespace which I do not want.
//Create our own namespaces for the output
XmlDocument XDoc = new XmlDocument();
XDoc.Load(#"D:\ServiceArea\SymbrioProjectIn\ProjectIn_Data\ProjectIn.xml");
XmlNodeList RemoveNodeProjectInRequest = XDoc.GetElementsByTagName("ProjectInRequest");
foreach (XmlNode Node in RemoveNodeProjectInRequest)
{
Node.ParentNode.RemoveChild(Node);
}
XDoc.Save(#"D:\ServiceArea\SymbrioProjectIn\ProjectIn_Data\ProjectIn.xml");
return response;
}
catch (Exception ex)
{
//throw new Exception(ex.Message);
throw new ApplicationException(ex.Message);
}
}
}
}
}
The file that is created looks like this
<?xml version="1.0"?>
<ProjectInRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="ProjectInRequest">
<SymbrioXMLProjectIn>
<Routing SourceValue="?" SourceType="?" DestinationValue="?" DestinationType="?" xmlns="http://www.symbrio.com/schemas/symbrioprojectin20133" />
<Project xmlns="http://www.symbrio.com/schemas/symbrioprojectin20133">
<CompanyCode>?</CompanyCode>
<DivisionCode>?</DivisionCode>
<MainProjectNo>?</MainProjectNo>
<ProjectNo>?</ProjectNo>
<ProjectName>?</ProjectName>
<ProjectType>?</ProjectType>
<PLCode>?</PLCode>
<EmploymentNo>?</EmploymentNo>
<Active>?</Active>
<ActiveStart>?</ActiveStart>
<ActiveEnd>?</ActiveEnd>
<DeliveryAddress>?</DeliveryAddress>
<DeliveryPostalCode>?</DeliveryPostalCode>
<DeliveryCity>?</DeliveryCity>
<DeliveryAddressNoteText>?</DeliveryAddressNoteText>
<AccountRef1>?</AccountRef1>
<AccountRef2>?</AccountRef2>
<AccountRef3>?</AccountRef3>
<AccountRef4>?</AccountRef4>
<RegisteredDate>?</RegisteredDate>
<Closed>?</Closed>
</Project>
</SymbrioXMLProjectIn>
</ProjectInRequest>
What I want is this,
<?xml version="1.0" encoding="utf-8"?>
<SymbrioXMLProjectIn>
<Routing SourceValue="str1234" SourceType="str1234" DestinationValue="str1234" DestinationType="str1234" />
<Project>
<CompanyCode>str1234</CompanyCode>
<DivisionCode>str1234</DivisionCode>
<MainProjectNo>str1234</MainProjectNo>
<ProjectNo>str1234</ProjectNo>
<ProjectName>str1234</ProjectName>
<ProjectType>str1234</ProjectType>
<PLCode>str1234</PLCode>
<EmploymentNo>str1234</EmploymentNo>
<Active>str1234</Active>
<ActiveStart>str1234</ActiveStart>
<ActiveEnd>str1234</ActiveEnd>
<DeliveryAddress>str1234</DeliveryAddress>
<DeliveryPostalCode>str1234</DeliveryPostalCode>
<DeliveryCity>str1234</DeliveryCity>
<DeliveryAddressNoteText>str1234</DeliveryAddressNoteText>
<AccountRef1>str1234</AccountRef1>
<AccountRef2>str1234</AccountRef2>
<AccountRef3>str1234</AccountRef3>
<AccountRef4>str1234</AccountRef4>
<RegisteredDate>str1234</RegisteredDate>
<Closed>str1234</Closed>
</Project>
</SymbrioXMLProjectIn>
ProjectInRequest - Node does not exist.
I might have missed something if so please point it out.
Thanks in advance.
use xpath to extract the record you wanted
//*[local-name()='SymbrioXMLProjectIn']
Nvm found the solution, I XmlSeralized wrong the correct way should have been
XmlSerializer ser = new XmlSerializer(request.SymbrioXMLProjectIn.GetType());
the bad way was
XmlSerializer ser = new XmlSerializer(request.GetType());
Will leave this here if anyone does a dummy misstake like me.

XXE: Improper Restriction of XML External Entity Reference With XDocument

So I am running into an issue when I run a security scan on my application. It turns out that I am failing to protect against XXE.
Here is a short snippet showing the offending code:
static void Main()
{
string inp = Console.ReadLine();
string xmlStr = ""; //This has a value that is much too long to put into a single post
if (!string.IsNullOrEmpty(inp))
{
xmlStr = inp;
}
XmlDocument xmlDocObj = new XmlDocument {XmlResolver = null};
xmlDocObj.LoadXml(xmlStr);
XmlNodeList measureXmlNodeListObj = xmlDocObj.SelectNodes("REQ/MS/M");
foreach (XmlNode measureXmlNodeObj in measureXmlNodeListObj)
{
XmlNode detailXmlNodeListObj = xmlDocObj.SelectSingleNode("REQ/DTD");
string measureKey = measureXmlNodeObj.Attributes["KY"].Value;
if (detailXmlNodeListObj.Attributes["MKY"].Value ==
measureKey) //Checking if selected MeasureKey is same
{
XmlNode filerNode = measureXmlNodeObj.SelectSingleNode("FS");
if (filerNode != null)
{
XDocument fixedFilterXmlObj = XDocument.Load(new StringReader(filerNode.OuterXml));
var measureFixedFilters = (from m in fixedFilterXmlObj.Element("FS").Elements("F")
select m).ToList();
foreach (var fixedFilter in measureFixedFilters)
{
var fixedFilterValues = (from m in fixedFilter.Elements("VS").Elements("V")
select m.Attribute("DESC").Value).ToList();
foreach (var value in fixedFilterValues)
{
Console.WriteLine(value.Trim());
}
}
}
}
}
Console.ReadLine();
}
According to Veracode, the line that unsafe is XDocument fixedFilterXmlObj = XDocument.Load(new StringReader(filerNode.OuterXml));
But it seems like according to Owsap, it should be safe:
Both the XElement and XDocument objects in the System.Xml.Linq library
are safe from XXE injection by default. XElement parses only the
elements within the XML file, so DTDs are ignored altogether.
XDocument has DTDs disabled by default, and is only unsafe if
constructed with a different unsafe XML parser.
So it seems like I am making the mistake of using an usafe XML Parser, opening XDocument to XXE.
I found a unit test that replicates the issue and also has a safe usage of XDocument but I can't seem to find what exactly my code is unsafe, because I do not use:
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse; // unsafe!
You can run my code to replicate the issue, but you should replace the line with the empty xmlStr with this value: here (too large for a single post)
I'm not sure how or why this works, but it does:
XDocument fixedFilterXmlObj;
using (XmlNodeReader nodeReader = new XmlNodeReader(filerNode))
{
nodeReader.MoveToContent();
fixedFilterXmlObj = XDocument.Load(nodeReader);
}

How to use Linq to XML to override existing XML file

I have a simple XML file structured like this
<?xml version="1.0" encoding="utf-8" ?>
<event>
<name>Test Event</name>
<date>06/09/1990</date>
<description>Birthday</description>
<blogURL></blogURL>
</event>
What I am trying to achieve is to have a form accept user input to create a new XML document with the same structure that will overwrite the old one.
The XML document is in the same directory as the ASPX page that will accept the input.
When the user navigates to the PressRelease.aspx page, it loads the values in the document.
var doc = XDocument.Load(Server.MapPath("~/PressSection.xml"));
string currentEventName = (string)doc.Descendants("name").Single();
string currentEventDate = (string)doc.Descendants("date").Single();
string currentEventDescription = (string)doc.Descendants("description").Single();
string currentEventLink = (string)doc.Descendants("blogURL").Single();
if (currentEventLink.ToString() == "")
{
CurrentEventURL.Text = "This event has no URL";
}
CurrentEventName.Text = currentEventName;
CurrentEventDescription.Text = currentEventDescription;
CurrentEventDate.Text = currentEventDate;
This works. I am grabbing the user input and doing simple validation like this
string newEventName = NewEventName.Text;
string newEventDescription = NewDescription.Text;
string newEventDate = NewDate.SelectedDate.Value.Date.ToString();
string newEventURL = NewURL.Text;
if (newEventName == "")
{
MessageBox.Text = "Please enter a valid event name";
MessageBox.CssClass = "event_error";
return;
}
if (newEventDescription == "")
{
MessageBox.Text = "Please enter a valid description";
MessageBox.CssClass = "event_error";
return;
}
if (newEventDate == null)
{
MessageBox.Text = "Please select a valid date";
MessageBox.CssClass = "event_error";
return;
}
And finally, I build and save the new XML document like this
//Create new document
XDocument newEventDocument = new XDocument(new XDeclaration("1.0", "utf-8", null));
//Addd root node
XElement RootNode = new XElement("Event");
//add root node to new document
newEventDocument.Add(RootNode);
//Add event name element then add it to the new document
XElement eName;
eName = new XElement("name");
eName.Value = newEventName;
newEventDocument.Root.Add(eName);
//Add event date element then add it to the new document
XElement eDate;
eDate = new XElement("date");
eDate.Value = newEventDate;
newEventDocument.Root.Add(eDate);
//Add event description element then add it to the new document
XElement eDescription;
eDescription = new XElement("description");
eDescription.Value = newEventDescription;
newEventDocument.Root.Add(eDescription);
//Add event URL element then add it to the new document
XElement eURL;
eURL = new XElement("blogURL");
eURL.Value = newEventURL;
newEventDocument.Root.Add(eURL);
//Finally, save the document
newEventDocument.Save("PressSection.xml", SaveOptions.None);
The issue that my program throws is an invalid permissions problem.
Access to the path 'C:\Program Files (x86)\IIS Express\PressSection.xml' is denied.
My question is, how do I save this in the root of my project directory, instead of IN MY IIS Express folder? I need it to overwrite the existing 'PressSection.xml' file that already exists in my project. Thanks!
This worked for it. Sorry for such an easy question
string path = Server.MapPath("PressSection.xml");
newEventDocument.Save(Path.Combine(path));

Read a connection string from a XML file

I've read about all I could get on how to read from an XML file, but I can't get anything done. I want to read a connectionstring from an XML file, plain and simple, nothing more.
My XML looks like
<?xml version="1.0" standalone="yes"?>
<connectionString>
<conn>"adsf"</conn>
</connectionString>
And I've tried varios way with
XmlDocument doc = new XmlDocument();
XmlTextReader reader = new XmlTextReader(xmlLoc);
while (reader.MoveToNextAttribute())
{
XmlNode a = doc.ReadNode(reader);
textBox1.Text = Text + a.Name;
}
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlLoc); //myXmlString is the xml file in string //copying xml to string: string myXmlString = xmldoc.OuterXml.ToString();
XmlNodeList xnList = xml.SelectNodes("/connectionString");
foreach (XmlNode xn in xnList)
{
XmlNode example = xn.SelectSingleNode("conn");
if (example != null)
{
string na = example["Name"].InnerText;
string no = example["NO"].InnerText;
}
}
I'm missing something and I'm not sure what, this should be a very simple task, but I can't get it done. Any help?
I'm trying to do it in a WIndows form application program.
I'm missing something?
Yes. .NET has a built in mechanism for storing your connection string, inside an App.config file, there is no reason to manually store it and parse it yourself.
Right click your project, go to Add -> New Item
Then, add a "Application Configuration File":
Once it opens, add a connectionStrings node to it, as follows:
// This is an example of a connectionString node. You may add child
// values to it as follows
<connectionStrings>
<add name="YourConnectionStringKey"
providerName="System.Data.SqlClient"
connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=YourDB;Trusted_Connection=Yes" />
</connectionStrings>
Then, you access it using ConfigurationManager.ConnectionStrings:
string connectionString =
ConfigurationManager.ConnectionStrings["YourConnectionStringKey"].ConnectionString;
You can use linq to xml.
var xmlStr = File.ReadAllText("fileName.xml");
var str = XElement.Parse(xmlStr);
var result = str.Elements("word").
Where(x => x.Element("connectionString").Value.Equals("conn"));
Console.WriteLine(result);
This might work (or may be some changes you need to make.
var x = XElement.Parse(#"<?xml version=""1.0"" standalone=""yes""?><connectionString><conn>adsf</conn></connectionString>");
// or var x = XElement.Load(#"c:\temp\my.xml");
var s = x.Element("conn").Value;
If you still have not found your answer then try this (a bit old school but it worked for me)
public class Common
{
public Common()
{
}
// public static string GetXML()
// {
// return #"C:\MyLocation\Connections.xml";
// }
public static string GetXMLconn(string strConn)
{
string xmlConStr = "";
//
string XMLconn = #"C:\Mylocation\Connections.xml";
// Get the Connection String from the XML file.
XmlTextReader textReader = new XmlTextReader(XMLconn);
textReader.Read();
while (textReader.Read())
{
// Read the currect element in the loop
textReader.MoveToElement();
// If the element name is correct then read and assign the connection string
if (textReader.Name == strConn)
{
xmlConStr = textReader.ReadString();
}
}
textReader.Close();
return xmlConStr;
}
}

Select xml file part with xpath and xdocument - C#/Win8

I am building a Windows 8 app, and I need to extract the whole XML node and its children as string from a large xml document, and the method that does that so far looks like this:
public string GetNodeContent(string path)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create("something.xml", settings))
{
reader.MoveToContent();
reader.Read();
XmlDocument doc = new XmlDocument();
doc.LoadXml(reader.ReadOuterXml());
IXmlNode node = doc.SelectSingleNode(path);
return node.InnerText;
}
}
When I pass any form of xpath, node gets the value of null. I'm using the reader to get the first child of root node, and then use XMLDocument to create one from that xml. Since it's Windows 8, apparently, I can't use XPathSelectElements method and this is the only way I can't think of. Is there a way to do it using this, or any other logic?
Thank you in advance for your answers.
[UPDATE]
Let's say XML has this general form:
<nodeone attributes...>
<nodetwo attributes...>
<nodethree attributes... />
<nodethree attributes... />
<nodethree attributes... />
</nodetwo>
</nodeone >
I expect to get as a result nodetwo and all of its children in the form of xml string when i pass "/nodeone/nodetwo" or "//nodetwo"
I've come up with this solution, the whole approach was wrong to start with. The problematic part was the fact that this code
reader.MoveToContent();
reader.Read();
ignores the namespace by itself, because it skips the root tag. This is the new, working code:
public static async Task<string> ReadFileTest(string xpath)
{
StorageFolder folder = await Package.Current.InstalledLocation.GetFolderAsync("NameOfFolderWithXML");
StorageFile xmlFile = await folder.GetFileAsync("filename.xml");
XmlDocument xmldoc = await XmlDocument.LoadFromFileAsync(xmlFile);
var nodes = doc.SelectNodes(xpath);
XmlElement element = (XmlElement)nodes[0];
return element.GetXml();
}

Categories

Resources