C# replace XML node with a new attribute and subnode - c#

I've got this xml file:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<startup><supportedRuntime version="v2.0.50727"/></startup>
<applicationSettings>
<MyApp.Settings>
...
...
</XNet.XManager.Properties.Settings>
</applicationSettings>
I need to replace the <startup> node with:
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
Which is the best way?

If you use LINQ to XML (it's an XML API rather than LINQ):
XDocument doc = XDocument.Load("dat.xml");
XElement startup1 = doc.Root.Element("startup");
startup1.Remove();
doc.Root.Add(new XElement("startup", new XAttribute("useLegacyV2RuntimeActivationPolicy", "true"),
new XElement("supportedRuntime", new XAttribute("version", "v4.0"),
new XAttribute("sku", ".NETFramework"),
new XAttribute("Version", "v4.5.2"))));
doc.Save("dat.xml");
Edit - as Jon Skeet suggested the proper way should be to use XElement.ReplaceWith :
XDocument doc = XDocument.Load("dat.xml");
XElement startup1 = doc.Root.Element("startup");
startup1.ReplaceWith(new XElement("startup", new XAttribute("useLegacyV2RuntimeActivationPolicy", "true"),
new XElement("supportedRuntime", new XAttribute("version", "v4.0"),
new XAttribute("sku", ".NETFramework"),
new XAttribute("Version", "v4.5.2"))));
doc.Save("dat.xml");

You can use the below code to do the same, where the element is being find and it is replaced with other.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path to your file");
string strXml =
#"<startup useLegacyV2RuntimeActivationPolicy='true'>
<supportedRuntime version='v4.0' sku='.NETFramework,Version=v4.5.2' />
</startup>";
XmlDocumentFragment xmlDocFragment = xmlDoc.CreateDocumentFragment();
xmlDocFragment.InnerXml = strXml;
xmlDoc.SelectSingleNode("startup").AppendChild(xmlDocFragment);
Update: Using LINQ. Working Tested Code
var doc = XDocument.Load(#"path to file");
string input = #"<startup useLegacyV2RuntimeActivationPolicy='true'>
<supportedRuntime version='v4.0' sku='.NETFramework,Version=v4.5.2' />
</startup>";
var replacement = XElement.Parse(input);
var nodeToReplace = doc.Descendants().Elements("startup").FirstOrDefault();
nodeToReplace.ReplaceWith(replacement);
doc.Save(#"path to file");
Console.WriteLine(doc);
Console.Read();

Related

Deserialize the xml configuration file present in different location

I am trying to Deserialize XML configuration file stored in %LOCALAPPDATA%\Configuration\Configuration.XML. I want to read the key value section and store it in the local variable.
The XML file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="FolderPath" value="C:\\Data" />
<add key="Name" value="xxx" />
<add key="Type" value="xxx" />
</appSettings>
</configuration>
ConfigurationManager.Appsettings.Get("FolderPath") does not work because the xml file is in the different location.
So I tried with the following code, which does not work.
var configFile = Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), "Configuration", "Configuration.XML")
var configuration = ConfigurationManager.OpenExeConfiguration(configFile);
var appSettings = configuration.GetSection("appSettings").SectionInformation;
Please help me to read the configuration.XML as shown above.
Thank you.
Thanks for your suggestions #jdweng.
I was able to solve the issue
var configFile = Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA"), "Configuration", "Configuration.XML"));
if (File.Exists(configFile))
{
ConfigurationDetails = new Dictionary<string, string>();
XDocument doc = XDocument.Load(configFile);
var elements = doc.Descendants("add");
foreach (var element in elements)
{
ConfigurationDetails.Add(element.Attribute("key").Value, element.Attribute("value").Value);
}
}

C# Referencing dll in subfolder

i can't figure out what i do wrong.
My project is like that :
MainProject
SubProject (referencing my desires DLL)
I have my app.config (of MainProject) :
<configuration>
<configSections>
<section name="DirectoryServerConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
<section name="GeneralConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="ServerConnectionConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings,Microsoft.Practices.EnterpriseLibrary.Caching"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="ReferencesDLL" />
</assemblyBinding>
</runtime>
<GeneralConfiguration configSource="ConfigFiles\GeneralConfiguration.config"/>
<cachingConfiguration configSource="ConfigFiles\cachingConfiguration.config"/>
<DirectoryServerConfiguration configSource="ConfigFiles\YPAdress.config"/>
<log4net configSource="ConfigFiles\log4net.config"/>
</configuration>
And my compile repository is like that :
Debug
ConfigFiles\ (All the .config files defined in the app.config)
ReferencesDLL\ (All the Dll needed by SubProject)
(All the others compiled files : .exe, .dll, etc..)
As you can see, i have my privatePath define, but when running the app, it can't find the needed dll for SubProject. (no problem for the .config files)
I don't know what i did wrong :( .
You could manually look for the assembly at the known location using an AppDomain.AssemblyResolve event handler:
static void Run()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
// Do work...
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var dllName = new AssemblyName(args.Name).Name + ".dll";
var assemblyPath = Assembly.GetExecutingAssembly().Location;
var referenceDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(assemblyPath), "ReferencesDLL"));
if (!referenceDirectory.Exists)
return null; // Can't find the reference directory
var assemblyFile = referenceDirectory.EnumerateFiles(dllName, SearchOption.TopDirectoryOnly).FirstOrDefault();
if (assemblyFile == null)
return null; // Can't find a matching dll
return Assembly.LoadFrom(assemblyFile.FullName);
}

C# XmlElement: Why does it always return Nulll?

I'm relatively new to C#, and just started using XmlElement and the SingleNode Method. For some reason "customSettings" keeps returning null, although the XML Document is being loaded correctly. I've checked that by loading it as a string. I've tried everything i could imagine so far including the attempt in the comment. Any help or suggestions are much appreciated. Here is my XML Doc:
EDIT: works with solution from CodingYoshi, but is there a better way?
EDIT: changed XML and code for NSGaga to resolve read only exception with NameValueCollection in user.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="users_fächer" type="System.Configuration.NameValueSectionHandler" />
</configSections>
<users_faecher>
<add key="user1" value="value1" />
<add key="user2" value="value2" />
</users_faecher>
</configuration>
Code:
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Users.config");
string new_fächer = input[1];
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = dir;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
ConfigurationSection myParamsSection = config.GetSection("users_faecher");
string myParamsSectionRawXml = myParamsSection.SectionInformation.GetRawXml();
XmlDocument sectionXmlDoc = new XmlDocument();
sectionXmlDoc.Load(new StringReader(myParamsSectionRawXml));
NameValueSectionHandler handler = new NameValueSectionHandler();
NameValueCollection users_fächer = handler.Create(null, null, sectionXmlDoc.DocumentElement) as NameValueCollection;
users_fächer.Set(user, new_fächer);
config.Save(ConfigurationSaveMode.Modified, true);
This will get you the first add element:
doc.ChildNodes[0].NextSibling.ChildNodes[1].ChildNodes[0].ChildNodes[0];
This will get you the first add element's value attribute.
doc.ChildNodes[0].NextSibling.ChildNodes[1]
.ChildNodes[0].ChildNodes[0].Attributes["value"].Value;

Saving and Loading Strongly-Typed DataSet from Settings

I added a strongly-typed DataSet object to my project. It's type name is DocRetrieverDataSet.
I also have in my project settings a row for a user-scope DataSet property named DocRetrieverDataSource to which I want to save an instance of DocRetrieverDataSet.
Here is boiled-down code:
using Settings = MyProjectNameSpace.Properties.Settings;
....
private DocRetrieverDataSet myDocRetrieverDataSet;
public myForm()
{
Initialize();
if (Settings.Default.DocRetrieverDataSource == null)
{
Settings.Default.DocRetrieverDataSource = new DocRetrieverDataSet();
Settings.Default.Save();
}
this.myDocRetrieverDataSet = (DocRetrieverDataSet)Settings.Default.DocRetrieverDataSource;
}
The first time I run it, when Settings.Default.DocRetrieverDataSource is null, it works fine! However, when I run it the second time, I get an InvalidCastException at
this.myDocRetrieverDataSet = (DocRetrieverDataSet)Settings.Default.DocRetrieverDataSource;
It says
Unable to cast object of type 'System.Data.DataSet' to type 'DocRetriever.DocRetrieverDataSet'.
The funny thing is that it doesn't have this problem the first time around. What is going on and how can I fix it?
MORE INFO:
Here's the relevant code from Settings.Designer.cs
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataSet DocRetrieverDataSource {
get {
return ((global::System.Data.DataSet)(this["DocRetrieverDataSource"]));
}
set {
this["DocRetrieverDataSource"] = value;
}
}
And from app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=abcdefghijkl" >
<section name="DocRetriever.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=abcdefghijkl" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<DocRetriever.Properties.Settings>
<setting name="SpoolDirectoryPath" serializeAs="String">
<value />
</setting>
<setting name="OutputDirectoryPath" serializeAs="String">
<value />
</setting>
</DocRetriever.Properties.Settings>
</userSettings>
</configuration>
You need to change the casting in your Settings file from DataSet to your DocRetrieverDataSet
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataSet DocRetrieverDataSource {
get {
return ((global::FullNamespace.DocRetrieverDataSet)(this["DocRetrieverDataSource"]));
}
set {
this["DocRetrieverDataSource"] = value;
}
}
You can also do it via the Settings Designer, just go to your property and browse for you class

Modify configProtectedData section in .config file

For the following:
<configProtectedData >
<providers>
<clear />
<add CertSubjectDistinguishedName="localhost" name="X509ProtectedConfigProvider" type="X509ProtectedConfig.X509ProtectedConfigProvider, X509ProtectedConfigProvider" />
</providers>
</configProtectedData>
how can i modify:
CertSubjectDistinguishedName="localhost"
and substitute "localhost" with something different?
I can't figure out how to read in "configProtectedData " section and modify it.
thanks
I solved it using XMLDocument:
string path = Path.Combine(targetDirectory, applicationExecutableName);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(path);
XmlNode node = xmlDoc.SelectSingleNode("configuration/configProtectedData/providers");
node.InnerXml = string.Format("<add CertSubjectDistinguishedName=\"{0}\" CertStoreName=\"{1}\" name=\"X509ProtectedConfigProvider\" type=\"ProtectedConfigProvider.X509ProtectedConfigProvider, X509ProtectedConfigProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=098027505e2ed139\" />", certSubject, certStoreName);
xmlDoc.Save(path);
If anyone knows a better way please post a sample. thanks

Categories