I need to validate the contents of an existing config file.
It looks like this:
<configuration>
<appSettings>
<Version>HB.2017.0</Version>
<FORMAT_VERSION>2.4</FORMAT_VERSION>
<MISC>Stuff.2014.0</MISC>
</appSettings>
</configuration>
I've been trying to write something in C# to read the file and assign the content of the Version and Format-Version and then validates if it is true or not but I keep getting a null pointer error.
Here's what I have so far:
public void ValidateConfigVersionSetting()
{
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\project.exe.config");
XmlNode node = doc.DocumentElement.SelectSingleNode("/Version");
string nodeContent = node.InnerText;
if (nodeContent.Equals("2017.0"))
{
Report.Success("Config", "Config is correct! 2017.");
}
else
{
Report.Failure("Config", "Config is not 2017.");
}
}
This is a code module for a Ranorex automation suite so the Validate.IsTrue comes from that. Is the way I store the innertext correct?
If that code is exactly as written, then you have a mistake here:
Validate.IsTrue(nodeContent="HB.2017.0", "Config is proper");
You're assigning the string to nodeContent, not comparing it.
Related
I currently have an xml file named PrioritizationSettings.config and I need to consolidate this into Web.config.
I have moved this directly into the Web.config as it is the same across all configurations.
I noticed that the project is using this old file path that no longer exists because I moved the XML directly into Web.config.
public static PrioritizationSettings LoadPrioritizeSettings()
{
return LoadPrioritizeSettings(AppDomain.CurrentDomain.BaseDirectory + "__Configs\\PrioritizationSettings.config");
}
I would like to be able to access the PrioritizationSettings inside of Web.config from here. So that instead of passing the entire XML file, I can just pass in the section of XML that now exists in Web.Config
Is there another way to do this without using ConfigurationManager.GetSection()? I have looked at this an I fear it may be far more involving. I just need to extract the XML.
This appears to be doing what I would like.
public static PrioritizationSettings LoadPrioritizeSettings()
{
return LoadPrioritizeSettings(AppDomain.CurrentDomain.BaseDirectory + "Web.config");
}
I now pass the entire Web.config file in. Inside of the LoadPrioritizeSettings I have the following code:
public static PrioritizationSettings LoadPrioritizeSettings(string configFile)
{
XmlReader xmlReader;
try { xmlReader = XmlReader.Create(configFile); }
catch (Exception ex) { throw ex; }
if (xmlReader == null)
return null;
XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlReader.Read();
xmlDoc.Load(xmlReader);
xmlReader.Close();
XmlNode xmlConfiguration = xmlDoc["configuration"];
if (xmlConfiguration == null)
throw new Exception("The root element (PrioritizationSettings) of the config file could not be found.");
XmlNode xmlPrioritizeSettings = xmlConfiguration["PrioritizationSettings"];
return prioritizeSettings;
}
So I am able to get the PrioritizationSetting node from the web.config.
I have an xml File
<configuration>
<MetisLinks>
<add key="MetisInternal" value="https://xyz.abc.com/" />
<add key="Hermes" value="https://hermes.abc.com/" />
<add key="umar" value="https://umar.abc.com/" />
</MetisLinks>
</configuration>
I need to add custom key and value in this MetisLink node using C#. Also if it already exists than overwrite.I searched for different solution but the exact was not on internet.
Thanks in advance!
This code should work for you:
string keyToAdd = "testKey";
string valueToAdd = "http://test.com";
XmlDocument doc = new XmlDocument();
doc.Load(#"D:\build.xml");
XmlNode root = doc.DocumentElement;
XmlElement existingMatchingElement = (XmlElement)root.SelectSingleNode(string.Format("//MetisLinks/add[#key='{0}']", keyToAdd));
if (existingMatchingElement != null)
{
existingMatchingElement.SetAttribute("value", valueToAdd);
}
else
{
XmlNode myNode = root.SelectSingleNode("MetisLinks");
var nodeToAdd = doc.CreateElement("add");
nodeToAdd.SetAttribute("key", keyToAdd);
nodeToAdd.SetAttribute("value", valueToAdd);
myNode.AppendChild(nodeToAdd);
}
doc.Save(#"D:\build.xml");
I created a file at D:\build.xml and copied your xml there, you could place your file containing that xml anywhere and just use that path in the code
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.
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'
}
I have this code that is supposed to load the attributes of an XML file to a string variable when the user clicks a button:
public void Button1_Click(object sender, EventArgs e)
{
XDocument doc = XDocument.Load("C:/Structure.xml");
Visit(doc.Root);
}
public static void Visit(XElement element)
{
string siteURL1 = element.Attribute("URL").Value;
string siteTitle1 = element.Attribute("siteTitle").Value;
string siteDescription1 = element.Attribute("siteDescription").Value;
string siteTemplate = element.Attribute("siteTemplate").Value;
string name = element.Attribute("type").Value;
Execute(name, siteURL1, siteTitle1, siteDescription1, siteTemplate);
}
But when I deploy the webpart, and click the button, I get the "NullreferenceException was unhandled by user code / Object reference not set to an instance of an object" errors. on:
string siteURL1 = element.Attribute("URL").Value;"
Any idea of what I may be doing wrong?
This is what the structure looks something like this:
<root>
<level1 name="level1A"
type="Private"
template="3
siteTitle="Private"
siteDescription="Private Site"
URL"private">
<level2 name="level2A">
<level3 name="level3A">
<level4 name="level4A">
<level5 name="level5A">
<level6 name="level6A">
<level7 name="level7A">
<level8 name="level8A"></level8>
</level7>
</level6>
</level5>
</level4>
</level3>
</level2>
</level1>
</root>
element.Attribute("URL") is null. You will need to inspect your data (XML) to see why that is. Your code assumes a certain structure that appears to be incorrect. Look closely at the element being passed in.
One of two things I could see being wrong...
Either the object element is null
or
the attribute URL doesn't exist for that element
Try something like...
var url = element.Attribute("URL");
string siteURL1;
if(url != null)
{
siteURL1 = url.Value;
}
Make some validations to assure doc is not null after XDocument doc = XDocument.Load("C:/Structure.xml");. Do you have access to that XML file on that location after deploying?
There are some other things that can be wrong, here a quick list:
your XML doesn't have an attribute XML.
your XML contains namespace information which you're not parsing
your XML root element is not the one you're expecting
Run debugging and look on the element you're passing into the Visit method.