XMLTextReader missing first element - c#

I'm writing a configuration storage method for one of my clients and they requested that it be in XML. I've managed to get it to work other than one issue; the first element is missing. My XML file is:
<?xml version="1.0" encoding="utf-8" ?>
<config>
<username>test</username>
<password>pass</password>
<autologin>true</autologin>
</config>
My Parsing command is:
void parseConfigFile()
{
while (configstr.Read())
{
if (configstr.IsStartElement())
{
config.Add(configstr.Name,configstr.ReadString());
}
}
}
and the result (configstr) is:
autologin = true
config =
password = pass

var document = XDocument.Load("file.xml");
var config = document.Root;
var userName = (string)config.Element("username");
var password = (string)config.Element("password");
var autologin = (bool)config.Element("autologin");

Related

Encrypting XML custom configuration file

I have this method which populates an object from my XML "custom configuration" config file:
public static BindingList<StationConfiguration> GetStationsFromConfigFile()
{
string xmlDocumentText = File.ReadAllText(GetConfigFilePath());
var doc = new XmlDocument();
doc.LoadXml(xmlDocumentText);
BindingList<StationConfiguration> stations = new BindingList<StationConfiguration>();
foreach (XmlNode node in doc.DocumentElement["StationsSection"].ChildNodes[0].ChildNodes)
{
stations.Add(
new StationConfiguration(
node.Attributes["Comment"].Value
, node.Attributes["FtpUsername"].Value
, node.Attributes["FtpPassword"].Value
, node.Attributes["DestinationFolderPath"].Value
));
}
return stations;
}
As you can see, I'm using File.ReadAllText to pull the contents of the XML config file into a String.
This all works well for a non-encrypted config file. But now I need to encrypt it. The way MSDN suggests doing that begins like this:
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
(Incidentally, when I look at the config.FilePath property, it shows me the correct path of the XML config file, just having an extra ".config" extension for some strange reason. It ends with ".exe.config.config" for some odd reason. But I digress...)
This is my StationConfigurationSection class:
public class StationConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("Stations", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(StationCollection),
AddItemName = "add",
ClearItemsName = "clear",
RemoveItemName = "remove")]
public StationCollection Stations
{
get
{
return (StationCollection)base["Stations"];
}
}
public override bool IsReadOnly()
{
return false;
}
}
My complete XML config file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="StationsSection" type="EcFtpClient.StationConfigurationSection, EcFtpClient" />
</configSections>
<StationsSection>
<Stations>
<add Comment="ABC" FtpUsername="eliezer" FtpPassword="secret" DestinationFolderPath="C:\Users\eliezer\Desktop\local dest" FtpTimeoutInSeconds="30" FtpHostname="ftp://192.168.1.100/" FtpFolderPath="" />
</Stations>
</StationsSection>
<startup>
<supportedRuntime version="v2.0.50727" />
</startup>
<appSettings>
<add key="NameOfService" value="ECClient" />
<add key="PollingFrequencyInSeconds" value="60" />
</appSettings>
</configuration>
I would like to use the MSDN System.Configuration approach, since it makes en/de-cryption very easy, but how can I blend their approach with what I have to make it work?
-- UPDATE --
I've got the loading of the file but am still stuck when it comes to saving the file.I've changed the loading method (at the very top of this question) to this:
public static BindingList<StationConfiguration> GetStationsFromConfigFile()
{
Configuration config = ConfigurationManager.OpenExeConfiguration(GetConfigFilePath());
StationConfigurationSection stationsConfig = (StationConfigurationSection)config.GetSection("StationsSection");
var stationCollection = ((StationCollection)stationsConfig.Stations);
BindingList<StationConfiguration> stationsToReturn = new BindingList<StationConfiguration>();
for (int index = 0; index < stationCollection.Count; index++)
{
stationsToReturn.Add(
new StationConfiguration(
stationCollection[index].Comment,
stationCollection[index].FtpUsername,
stationCollection[index].FtpPassword,
stationCollection[index].DestinationFolderPath)
);
return stationsToReturn;
}
What that gets me is the ability to load a file regardless of whether it's encrypted or not - it loads successfully. That's great.
But I'm still not sure how to get saving working. Here's my save method:
public static void SaveStationsToConfigFile(BindingList<StationConfiguration> updatedStations, bool isConfigToBeEncrypted)
{
string configFilePath = GetConfigFilePath();
var xDoc = XDocument.Load(configFilePath);
var xDeclaration = xDoc.Declaration;
var xElement = xDoc.XPathSelectElement("//StationsSection/Stations");
// clear out existing station configurations
xDoc.Descendants("Stations").Nodes().Remove();
foreach (var station in updatedStations)
{
xElement.Add(new XElement("add",
new XAttribute("Comment", station.Station),
new XAttribute("FtpUsername", station.Username),
new XAttribute("FtpPassword", station.Password),
new XAttribute("DestinationFolderPath", station.FolderName),
new XAttribute("FtpTimeoutInSeconds", 30),
new XAttribute("FtpHostname", GetEnvironmentAppropriateFtpHostName()),
new XAttribute("FtpFolderPath", GetEnvironmentAppropriateFtpFolderPath())
));
}
xDoc.Declaration = xDeclaration;
xDoc.Save(configFilePath);
}
And in order to save it with the protection/encrpytion, I need to do something like this - in other words, using the System.Configuration.Configuration objects:
stationsConfig.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
stationsConfig.SectionInformation.ForceSave = true;
objConfig.Save(ConfigurationSaveMode.Modified);
But I'm currently still doing the save with XDocument.Save...
Is there a way to convert my XDocument into a System.Configuration.Configuration compatible object?The hack that comes to mind is after the call to XDocument.Save - to load it and save it again using the System.Configuration.Configuration stuff. But that's a hack...
I believe you have to play along with the configuration settings framework. Rather than trying to open the xml file yourself, you need to create a descendant of ConfigurationSection.
That way it will read and write from the encrypted configuration for you.
It looks like you already started that route (EcFtpClient.StationConfigurationSection), but you didn't include any of that code.

Simple XML file to store info

I want to create a simple XML file to store some settings that can be easily changed in a text editor. This is my XML file:
<connection>
<hostname>the.host.name</hostname>
<port>1000</port>
<database>theDbName</database>
</connection>
I am trying to use Linq to XML now to read that info into my program, but it gives me an error:
Could not find an implementation of the query pattern for source type 'System.Xml.Linq.XElement'. 'Select' not found.
My code is as follows:
XElement root = XElement.Load(".\\Resources\\Connection.xml");
string host = from el in root.Element("hostname") select el.Element("text");
XDocument xDoc = XDocument.Load(".\\Resources\\Connection.xml");
string host = (string)xDoc.Root.Element("hostname");
I think you're confused on how your XML structure should look and the way you read that XML with LinQ. First of all place your connection-element in a root called connections. Your XML looks like this now:
<?xml version="1.0" encoding="utf-8"?>
<connections>
<connection>
<hostname>the.host.name</hostname>
<port>1000</port>
<database>theDbName</database>
</connection>
</connections>
Now you can select the elements under that root and read all the data from it. Example code looks like following:
var root = XElement.Load(".\\Resources\\Connection.xml");
var connection = root.Elements("connection").FirstOrDefault();
if(connection != null)
{
var host = connection.Element("hostname").Value;
//do something with 'host'
}
Update:
If you don't want a root element 'connections', you can omit it and use following code to read the XML:
var xmlDoc = XDocument.Load("G:\\Connection.xml");
var connection = xmlDoc.Descendants("connection").FirstOrDefault();
if(connection != null)
{
var host = connection.Element("hostname").Value;
//do something with 'host'
}

Append subchildnode

I'm stuck with some task here. I want to write to xml file.
Here is the file:
<?xml version="1.0" encoding="utf-8"?>
<lists>
<plannedList>
<Skift-a></Skift-a>
<Skift-b></Skift-b>
<Skift-c></Skift-c>
<Skift-d></Skift-d>
<Skift-e></Skift-e>
</plannedList>
<requestedList>
<Skift-a></Skift-a>
<Skift-b></Skift-b>
<Skift-c></Skift-c>
<Skift-d></Skift-d>
<Skift-e></Skift-e>
</requestedList>
</lists>
then i want to use c# and for example dataset or an other method to add a jobID under the loggedIn Skift. so if Skift-a is logged on, and press a button, i want the xml file to look like this:
<?xml version="1.0" encoding="utf-8"?>
<lists>
<plannedList>
<Skift-a></Skift-a>
<Skift-b></Skift-b>
<Skift-c></Skift-c>
<Skift-d></Skift-d>
<Skift-e></Skift-e>
</plannedList>
<requestedList>
<Skift-a></Skift-a>
<jobID>1<jobID/>
<Skift-b></Skift-b>
<Skift-c></Skift-c>
<Skift-d></Skift-d>
<Skift-e></Skift-e>
</requestedList>
</lists>
And here is what I've tried so far:
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(this.Page.Server.MapPath("~/StoredData/JobsByUsers.xml"));
XmlNode Skift = xmldoc.SelectSingleNode("/requestedList/" + User.Identity.Name.ToString());
XmlNode newJobID = xmldoc.CreateNode(XmlNodeType.Element, "jobID", null);
newJobID.InnerText = topID.ToString();
Skift.AppendChild(newJobID);
xmldoc.Save(this.Page.Server.MapPath("~/StoredData/JobsByUsers.xml"));

Parsing URL/web-service

I made a request to a third party API and it gives me the following response in XML.
<?xml version="1.0" ?>
<abc>
<xyz>
<code>-112</code>
<message>No such device</message>
</xyz>
</abc>
I read this using this code.
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/abc/xyz");
string code = node.SelectSingleNode("code").InnerText;
string msg = node.SelectSingleNode("message").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
But I get an error on this line:
string code = node.SelectSingleNode("code").InnerText;
Error is:
Object reference not set to an instance of an object.
I changed the first line of your XML file into:
<?xml version="1.0"?>
to make it valid XML. With this change, your code works for me. Without the change, the parser throws an exception.
You can use LINQ to XML (if confortable):
XDocument doc = XDocument.Load(url);
var selectors = (from elements in doc.Elements("abc").Elements("xyz")
select elements).FirstOrDefault();
string code = selectors.Element("code").Value;
string msg = selectors.Element("message").Value;
As you've given it, there doesn't seem to be anything wrong with your code Edit : Your declaration is wrong, as svinja pointed out, and your xml won't even load into the XmlDocument.
However, I'm guessing that your xml is more complicated, and there is at least one namespace involved, which would cause the select to fail.
It isn't pretty, but what you can do is use namespace agnostic xpath to locate your nodes to avoid using a XmlNamespaceManager:
XmlDocument doc = new XmlDocument();
doc.Load("*** url ***");
XmlNode node = doc.SelectSingleNode("/*[local-name()='abc']/*[local-name()='xyz']");
string code = node.SelectSingleNode("*[local-name()='code']").InnerText;
string msg = node.SelectSingleNode("*[local-name()='message']").InnerText;
Response.Write("Code: " + code);
Response.Write("Message: "+ msg);
Edit - Elaboration
Your code works fine if you correct the declaration to <?xml version="1.0"?>
However, if you introduce namespaces into the mix, your code will fail unless you use namespace managers appropriately.
My agnostic xpath above will also parse an xml document like so:
<?xml version="1.0"?>
<abc xmlns="foo">
<xyz xmlns="bar">
<code xmlns="bas">-112</code>
<message xmlns="xyz">No such device</message>
</xyz>
</abc>
<?xml version="1.0">
<abc>
<xyz>
<code>-112</code>
<message> No such device </message>
</xyz>
</abc>
try to set a list:
XmlNodeList nodeList = root.SelectNodes("/abc/xyz");
then read all the nodes and get their text:
foreach(XmlNode node in nodeList)
{
if(node.Name == "code")
{
string code = node.InnerText;
}
else
if(node.Name == "message")
{
string msg = node.InnerText;
}
}
[XmlRoot("abc")]
public class Entity
{
[XmlElement("xyz")]
public SubEntity SubEntity { get; set; }
}
public class SubEntity
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("message")]
public string Message { get; set; }
}
And use standart xmlserializer
var xmlSerializer = new XmlSerializer(typeof(Entity));
var result = xmlSerializer.Deserialize(new XmlTextReader("*** url ***"));
Response.Write("Code: " + result.SubEntity.Code);
Response.Write("Message: "+ result.SubEntity.Message);

Extracting values from XML returned by azure service management API

i have tried several methods of trying to extract values from an XML file but none of them seem to work. I am using C#. The XML Is as follows
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>label</Label>
</HostedServiceProperties>
</HostedService>
I would like to retrieve
hosted-service-url,
hosted-service-name,
description,
location,
affinity-group and
label
What would be the best of way of retrieving these values?
Edit :
Thanks L.B that method works perfectly. However i have just been told i will have to use the larger XML that is below.
<?xml version="1.0" encoding="utf-8"?>
<HostedService xmlns="http://schemas.microsoft.com/windowsazure">
<Url>hosted-service-url</Url>
<ServiceName>hosted-service-name</ServiceName>
<HostedServiceProperties>
<Description>description</Description>
<Location>location</Location>
<AffinityGroup>affinity-group</AffinityGroup>
<Label>base-64-encoded-name-of-the-service</Label>
</HostedServiceProperties>
<Deployments>
<Deployment>
<Name>deployment-name</Name>
<DeploymentSlot>deployment-slot</DeploymentSlot>
<PrivateID>deployment-id</PrivateID>
<Status>deployment-status</Status>
<Label>base64-encoded-deployment-label</Label>
<Url>deployment-url</Url>
<Configuration>base-64-encoded-configuration-file</Configuration>
<RoleInstanceList>
<RoleInstance>
<RoleName>role-name</RoleName>
<InstanceName>role-instance-name</InstanceName>
<InstanceStatus>instance-status</InstanceStatus>
</RoleInstance>
</RoleInstanceList>
<UpgradeDomainCount>upgrade-domain-count</UpgradeDomainCount>
<RoleList>
<Role>
<RoleName>role-name</RoleName>
<OsVersion>operating-system-version</OsVersion>
</Role>
</RoleList>
<SdkVersion>sdk-version-used-to-create-package</SdkVersion>
<InputEndpointList>
<InputEndpoint>
<RoleName>role-name</RoleName>
<Vip>virtual-ip-address</Vip>
<Port>port-number</Port>
</InputEndpoint>
…
</InputEndpointList>
<Locked>deployment-write-allowed-status</Locked>
<RollbackAllowed>rollback-operation-allowed</RollbackAllowed>
</Deployment>
</Deployments>
</HostedService>
My final question is, there is several repeated tags, such as ,
how can i differentiate between them?
you can use Xml to Linq to parse your xml string. For ex,
var xElem = XElement.Load(new StringReader(xml));
var ns = XNamespace.Get("http://schemas.microsoft.com/windowsazure");
var obj = new
{
ServiceName = xElem.Descendants(ns + "ServiceName").First().Value,
Description = xElem.Descendants(ns + "Description").First().Value,
};
or you can use XmlSerializer
XmlSerializer xs = new XmlSerializer(typeof(HostedService), "http://schemas.microsoft.com/windowsazure");
var obj2 = (HostedService)xs.Deserialize(new StringReader(xml));
public class HostedService
{
public string Url;
public string ServiceName;
public HostedServiceProperties HostedServiceProperties;
}
public class HostedServiceProperties
{
public string Description;
public string Location;
public string AffinityGroup;
public string Label;
}
Maybe you can try samples from XmlDocument ( http://msdn.microsoft.com/en-us/library/d271ytdx.aspx) and and LINQ to XML -( http://msdn.microsoft.com/en-us/library/bb669152.aspx) first and than apply it to your case.

Categories