How to remove List parent node from xml? - c#

I Have tried to remove scheme-details1 from Below XML using the below code but unable to remove it.
XmlActionResult xmlActionResult = new XmlActionResult(schememaster);
foreach (XElement element in xmlActionResult._document.Elements())
{
xmlActionResult._document.Elements("scheme-details1").Remove();
}
where xmlActionResult._document is XDocument.
Below is my XML response:-
<scheme-master>
<general-information>
<schemecode>XXXXX</schemecode>
<year>2020</year>
<month>04</month>
<requestid>0008052020</requestid>
</general-information>
<scheme-details1>
<scheme-details>
<location>
<district_code>503</district_code>
<district_name>Chittoor</district_name>
<state_code>28</state_code>
<state_name>Andhra pradesh</state_name>
</location>
<beneficiary-details>
<no_of_beneficiaries_normative_central_and_state_share>10000</no_of_beneficiaries_normative_central_and_state_share>
<no_of_additional_beneficiaries_supported_by_state>0</no_of_additional_beneficiaries_supported_by_state>
<total_no_of_beneficiaries>10000</total_no_of_beneficiaries>
<no_of_beneficiaries_record_digitized>10000</no_of_beneficiaries_record_digitized>
<no_of_authenticated_seeded_beneficiaries>10000</no_of_authenticated_seeded_beneficiaries>
<no_of_beneficiaries_whom_mobile_no_captured>10000</no_of_beneficiaries_whom_mobile_no_captured>
</beneficiary-details>
<fundtransfer-details>
<central_share_fund_transferred_cash>10000</central_share_fund_transferred_cash>
<normative_state_share_fund_transferred_cash>0</normative_state_share_fund_transferred_cash>
<additional_state_share_fund_transferred_cash>0</additional_state_share_fund_transferred_cash>
<state_share_fund_transferred_to_additional_beneficiaries_cash>0</state_share_fund_transferred_to_additional_beneficiaries_cash>
<total_fund_transferred_cash>10000</total_fund_transferred_cash>
<central_share_expenditure_incurred_inkind>0</central_share_expenditure_incurred_inkind>
<normative_state_share_expenditure_incurred_inkind>0</normative_state_share_expenditure_incurred_inkind>
<additional_state_share_expenditure_incurred_inkind>0</additional_state_share_expenditure_incurred_inkind>
<state_share_expenditure_incurred_to_additional_beneficiaries_inkind>0</state_share_expenditure_incurred_to_additional_beneficiaries_inkind>
<total_expenditure_incurred_inkind>0</total_expenditure_incurred_inkind>
</fundtransfer-details>
<transaction-details>
<total_no_transactions_electronic_modes_cash>10000</total_no_transactions_electronic_modes_cash>
<payment_electronic_modes_cash>10000</payment_electronic_modes_cash>
<total_no_transactions_other_modes_cash>0</total_no_transactions_other_modes_cash>
<payment_other_modes_cash>0</payment_other_modes_cash>
<quantity_transferred_inkind>0</quantity_transferred_inkind>
<no_of_authenticated_transactions_inkind>0</no_of_authenticated_transactions_inkind>
<dbt_expenditure_incurred_inkind>0</dbt_expenditure_incurred_inkind>
</transaction-details>
</scheme-details>
<scheme-details>
<location>
<district_code>504</district_code>
<district_name>Cuddapah (YSR Kadapa)</district_name>
<state_code>28</state_code>
<state_name>Andhra pradesh</state_name>
</location>
<beneficiary-details>
<no_of_beneficiaries_normative_central_and_state_share>10000</no_of_beneficiaries_normative_central_and_state_share>
<no_of_additional_beneficiaries_supported_by_state>0</no_of_additional_beneficiaries_supported_by_state>
<total_no_of_beneficiaries>10000</total_no_of_beneficiaries>
<no_of_beneficiaries_record_digitized>10000</no_of_beneficiaries_record_digitized>
<no_of_authenticated_seeded_beneficiaries>10000</no_of_authenticated_seeded_beneficiaries>
<no_of_beneficiaries_whom_mobile_no_captured>10000</no_of_beneficiaries_whom_mobile_no_captured>
</beneficiary-details>
<fundtransfer-details>
<central_share_fund_transferred_cash>10000</central_share_fund_transferred_cash>
<normative_state_share_fund_transferred_cash>0</normative_state_share_fund_transferred_cash>
<additional_state_share_fund_transferred_cash>0</additional_state_share_fund_transferred_cash>
<state_share_fund_transferred_to_additional_beneficiaries_cash>0</state_share_fund_transferred_to_additional_beneficiaries_cash>
<total_fund_transferred_cash>10000</total_fund_transferred_cash>
<central_share_expenditure_incurred_inkind>0</central_share_expenditure_incurred_inkind>
<normative_state_share_expenditure_incurred_inkind>0</normative_state_share_expenditure_incurred_inkind>
<additional_state_share_expenditure_incurred_inkind>0</additional_state_share_expenditure_incurred_inkind>
<state_share_expenditure_incurred_to_additional_beneficiaries_inkind>0</state_share_expenditure_incurred_to_additional_beneficiaries_inkind>
<total_expenditure_incurred_inkind>0</total_expenditure_incurred_inkind>
</fundtransfer-details>
<transaction-details>
<total_no_transactions_electronic_modes_cash>10000</total_no_transactions_electronic_modes_cash>
<payment_electronic_modes_cash>10000</payment_electronic_modes_cash>
<total_no_transactions_other_modes_cash>0</total_no_transactions_other_modes_cash>
<payment_other_modes_cash>0</payment_other_modes_cash>
<quantity_transferred_inkind>0</quantity_transferred_inkind>
<no_of_authenticated_transactions_inkind>0</no_of_authenticated_transactions_inkind>
<dbt_expenditure_incurred_inkind>0</dbt_expenditure_incurred_inkind>
</transaction-details>
</scheme-details>
<scheme-details>
<location>
<district_code>503</district_code>
<district_name>Chittoor</district_name>
<state_code>28</state_code>
<state_name>Andhra pradesh</state_name>
</location>
<beneficiary-details>
<no_of_beneficiaries_normative_central_and_state_share>10000</no_of_beneficiaries_normative_central_and_state_share>
<no_of_additional_beneficiaries_supported_by_state>0</no_of_additional_beneficiaries_supported_by_state>
<total_no_of_beneficiaries>10000</total_no_of_beneficiaries>
<no_of_beneficiaries_record_digitized>10000</no_of_beneficiaries_record_digitized>
<no_of_authenticated_seeded_beneficiaries>10000</no_of_authenticated_seeded_beneficiaries>
<no_of_beneficiaries_whom_mobile_no_captured>10000</no_of_beneficiaries_whom_mobile_no_captured>
</beneficiary-details>
<fundtransfer-details>
<central_share_fund_transferred_cash>10000</central_share_fund_transferred_cash>
<normative_state_share_fund_transferred_cash>0</normative_state_share_fund_transferred_cash>
<additional_state_share_fund_transferred_cash>0</additional_state_share_fund_transferred_cash>
<state_share_fund_transferred_to_additional_beneficiaries_cash>0</state_share_fund_transferred_to_additional_beneficiaries_cash>
<total_fund_transferred_cash>10000</total_fund_transferred_cash>
<central_share_expenditure_incurred_inkind>0</central_share_expenditure_incurred_inkind>
<normative_state_share_expenditure_incurred_inkind>0</normative_state_share_expenditure_incurred_inkind>
<additional_state_share_expenditure_incurred_inkind>0</additional_state_share_expenditure_incurred_inkind>
<state_share_expenditure_incurred_to_additional_beneficiaries_inkind>0</state_share_expenditure_incurred_to_additional_beneficiaries_inkind>
<total_expenditure_incurred_inkind>0</total_expenditure_incurred_inkind>
</fundtransfer-details>
<transaction-details>
<total_no_transactions_electronic_modes_cash>10000</total_no_transactions_electronic_modes_cash>
<payment_electronic_modes_cash>10000</payment_electronic_modes_cash>
<total_no_transactions_other_modes_cash>0</total_no_transactions_other_modes_cash>
<payment_other_modes_cash>0</payment_other_modes_cash>
<quantity_transferred_inkind>0</quantity_transferred_inkind>
<no_of_authenticated_transactions_inkind>0</no_of_authenticated_transactions_inkind>
<dbt_expenditure_incurred_inkind>0</dbt_expenditure_incurred_inkind>
</transaction-details>
</scheme-details>
<scheme-details>
<location>
<district_code>504</district_code>
<district_name>Cuddapah (YSR Kadapa)</district_name>
<state_code>28</state_code>
<state_name>Andhra pradesh</state_name>
</location>
<beneficiary-details>
<no_of_beneficiaries_normative_central_and_state_share>10000</no_of_beneficiaries_normative_central_and_state_share>
<no_of_additional_beneficiaries_supported_by_state>0</no_of_additional_beneficiaries_supported_by_state>
<total_no_of_beneficiaries>10000</total_no_of_beneficiaries>
<no_of_beneficiaries_record_digitized>10000</no_of_beneficiaries_record_digitized>
<no_of_authenticated_seeded_beneficiaries>10000</no_of_authenticated_seeded_beneficiaries>
<no_of_beneficiaries_whom_mobile_no_captured>10000</no_of_beneficiaries_whom_mobile_no_captured>
</beneficiary-details>
<fundtransfer-details>
<central_share_fund_transferred_cash>10000</central_share_fund_transferred_cash>
<normative_state_share_fund_transferred_cash>0</normative_state_share_fund_transferred_cash>
<additional_state_share_fund_transferred_cash>0</additional_state_share_fund_transferred_cash>
<state_share_fund_transferred_to_additional_beneficiaries_cash>0</state_share_fund_transferred_to_additional_beneficiaries_cash>
<total_fund_transferred_cash>10000</total_fund_transferred_cash>
<central_share_expenditure_incurred_inkind>0</central_share_expenditure_incurred_inkind>
<normative_state_share_expenditure_incurred_inkind>0</normative_state_share_expenditure_incurred_inkind>
<additional_state_share_expenditure_incurred_inkind>0</additional_state_share_expenditure_incurred_inkind>
<state_share_expenditure_incurred_to_additional_beneficiaries_inkind>0</state_share_expenditure_incurred_to_additional_beneficiaries_inkind>
<total_expenditure_incurred_inkind>0</total_expenditure_incurred_inkind>
</fundtransfer-details>
<transaction-details>
<total_no_transactions_electronic_modes_cash>10000</total_no_transactions_electronic_modes_cash>
<payment_electronic_modes_cash>10000</payment_electronic_modes_cash>
<total_no_transactions_other_modes_cash>0</total_no_transactions_other_modes_cash>
<payment_other_modes_cash>0</payment_other_modes_cash>
<quantity_transferred_inkind>0</quantity_transferred_inkind>
<no_of_authenticated_transactions_inkind>0</no_of_authenticated_transactions_inkind>
<dbt_expenditure_incurred_inkind>0</dbt_expenditure_incurred_inkind>
</transaction-details>
</scheme-details>
</scheme-details1>
</scheme-master>

You are looping through the XElement element root node(s) of your XDocument but then try to remove the <scheme-details1> nodes from the document itself. Instead, remove them from the current element:
foreach (var element in xmlActionResult._document.Elements())
{
//xmlActionResult._document.Elements("scheme-details1").Remove(); // FIXED
element.Elements("scheme-details1").Remove();
}
Or, since a well-formed XML document always has exactly one root element, you could eliminate the loop through the root elements and just do:
xmlActionResult._document.Root.Elements("scheme-details1").Remove();
Demo here.
Update
Remove() removes a collection of nodes and all their descendants from their parent(s). If you want to remove the <scheme-details1> node and bubble all its children up to its parent node, you can create the following extension methods:
public static class XNodeExtensions
{
public static void RemoveAndMoveChildrenUp (this IEnumerable<XNode> source)
{
foreach (var node in source.ToList())
node.RemoveAndMoveChildrenOfTypeUp<XNode>();
}
public static void RemoveAndMoveChildElementsUp (this IEnumerable<XNode> source)
{
foreach (var node in source.ToList())
node.RemoveAndMoveChildrenOfTypeUp<XElement>();
}
public static void RemoveAndMoveChildrenOfTypeUp<TChild>(this XNode node) where TChild : XNode
{
var parent = node.Parent;
if (node is XContainer container)
{
for (XNode child = container.FirstNode, nextChild = child?.NextNode; child != null; child = nextChild, nextChild = child?.NextNode)
{
if (child is TChild)
{
child.Remove(); // Remove the child first to prevent cloning when added to the parent
parent.Add(child);
}
}
}
node.Remove();
}
}
And then do:
xmlActionResult._document.Root.Elements("scheme-details1").RemoveAndMoveChildrenUp();
Or, if <scheme-details1> were a mixed content node and you wanted to bubble up only the child elements, use RemoveAndMoveChildElementsUp().
Demo fiddle #2 here.

The above-posted answer also works and the one has got with also works for me by replacing the tag from XML.
XDocument doc = new XDocument();
using (var writer = doc.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(obj.GetType());
serializer.WriteObject(writer, obj);
}
string xml = doc.ToString();
xml = xml.Replace("<scheme-details1>", "");
xml = xml.Replace("</scheme-details1>", "");
doc = XDocument.Parse(xml);

Related

trying to delete xmlns namespaces of nodes

I've got some problems with xml messages and c#.
The problem is a root element with no namespaces and all the namespaces are in the nodes.
I've got a part of the script running to delete the namespaces so I can read all the xml messages that will be sent to the webserver.
The message that gives the problems:
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetOrderResponseRequest xmlns="http://www.edibulb.nl/XML/Order:2">
<Header>
<UserName xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">FBT_000390</UserName>
<Password xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">1FWcgwrx9</Password>
<MessageID xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" schemeDataURI="8719604082016">8719604082016100376</MessageID>
<MessageDateTime xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" format="304">20170523090413+02:00</MessageDateTime>
</Header>
<Body>
<AgentParty>
<PrimaryID xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" schemeID="251" schemeAgencyName="EBC">8719604178115</PrimaryID>
</AgentParty>
<GetOrderResponseDetails>
<MutationDateTime xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2" format="304">20170510000000+02:00</MutationDateTime>
<BuyerParty xmlns="urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2">
<PrimaryID xmlns="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:3" schemeID="251" schemeAgencyName="EBC">8719604082016</PrimaryID>
</BuyerParty>
</GetOrderResponseDetails>
</Body>
</GetOrderResponseRequest>
</soap:Body>
</soap:Envelope>
And here is the part of the script to translate on the webservice
the code below works perfectly fine if there are prefixes involved.
but it wont work with the xml defined above.
Here is the class that I call from the webservice.
First I check in the xml string if there are any prefixes.
RemoveNamespace remove = new RemoveNamespace();
public string orderrequest(string xmldoc, string ivbglns, bool success)
{
if (success == true)
{
if (xmldoc.Contains(":UserName"))
{
string xdoc = remove.removeall(xmldoc);
docx = new XmlDocument();
docx.LoadXml(xdoc);
}
else if(xmldoc.Contains("<UserName xmlns"))
{
string xdoc = remove.removexlmns(xmldoc);
docx = new XmlDocument();
docx.LoadXml(xmldoc);
}
// rest of the code for the response
}
}
and below the RemoveNameSpace part:
public string removeall(string xdoc)
{
string docx = RemoveAllNamespaces(xdoc);
return docx;
}
public static string RemoveAllNamespaces(string xmldoc)
{
XElement documentwithoutns = XRemoveAllNamespaces(XElement.Parse(xmldoc));
return documentwithoutns.ToString();
}
private static XElement XRemoveAllNamespaces(XElement Xmldoc)
{
if (!Xmldoc.HasElements)
{
XElement element = new XElement(Xmldoc.Name.LocalName);
element.Value = Xmldoc.Value;
foreach (XAttribute attribute in Xmldoc.Attributes())
element.Add(attribute);
return element;
}
return new XElement(Xmldoc.Name.LocalName, Xmldoc.Elements().Select(el => XRemoveAllNamespaces(el)));
}
public string removexlmns(string xdoc)
{
string pattern = "\\s+xmlns\\s*(:\\w)?\\s*=\\s*\\\"(?<url>[^\\\"]*)\\\"";
MatchCollection matchcol = Regex.Matches(xdoc, pattern);
foreach (Match m in matchcol)
{
xdoc = xdoc.Replace(m.ToString(), "");
}
return xdoc;
}
The error it returns is: The Prefix "cannot be redefined from" to 'urn:ebl:edibulb:xml:data:draft:ReusableAggregateBusinessInformationEntity:2' within the same start element tag.
I'm in search for a solution for this. The xml from above is a message thats beyond my control.
with Kind regards
Stephan
I would very strongly suggest you use the namespaces in whatever XML processing you are doing after this. Stop trying to remove them!
If you must remove them, it's worth noting that XElement.Name is mutable. You can remove all the namespace declarations and set all the names to their local names.
var doc = XDocument.Parse(xml);
doc.Descendants()
.Attributes()
.Where(x => x.IsNamespaceDeclaration)
.Remove();
foreach (var element in doc.Descendants())
{
element.Name = element.Name.LocalName;
}
See this fiddle for a demo.

How to add just the base node in a listview as an item, is getting all the info

I'm trying to read an XML and get the base node to add it in a listview, but is getting all the information of those nodes and is adding those information as an item.
This is my XML:
<?xml version="1.0" encoding="utf-8"?>
<Servers>
<MYSERVER>
<Host>xxx.xxx.xxx.xxx</Host>
<User>MyUser</User>
<Password>MyPassword</Password>
<Port>25</Port>
</MYSERVER>
</Servers>
I just want to add "MYSERVER" into the listview but is adding all the information (Host, User, Password and Port).
This is What I'm doing:
private void frmTransmissionManagement_Load(object sender, EventArgs e)
{
XMLDoc.Load(XMLInterfaces);
foreach (XmlNode node in XMLDoc.SelectSingleNode("Servers"))
{
lvConnections.Items.Add(node.InnerText, 0);
}
}
Any idea?
Thanks
I like XDocument, too. This code worked for me, though. I believe this is what you want.
foreach (XmlNode node in XMLDoc.ChildNodes)
{
if (node.Name == "Servers")
{
foreach (XmlNode serverNode in node)
{
string s = serverNode.Name;
MessageBox.Show(s);
}
}
}
Using XDcoument from System.Xml.Linq you can use something like the following:
doc.Element("Servers").Elements().Select(x => x.Name);
This returns just the string (actually in this case it's an IEnumerable with a single value): "MYSERVER".
A quick sample program I wrote demonstrates:
static void Main(string[] args)
{
var doc = System.Xml.Linq.XDocument.Parse("<?xml version=\"1.0\" encoding=\"utf-8\"?> <Servers> <MYSERVER> <Host>xxx.xxx.xxx.xxx</Host> <User>MyUser</User> <Password>MyPassword</Password> <Port>25</Port> </MYSERVER> </Servers>");
var servers = doc.Element("Servers").Elements().Select(x => x.Name);
foreach(var s in servers)
{
Console.WriteLine(s);
}
Console.ReadLine();
}

Selecting inner text of XML nodes and adding to list

I have the following XML file called file.xml:
<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config">
<defaults>
<serializer>pof</serializer>
</defaults>
<caching-scheme-mapping>
<cache-mapping>
<cache-name>broadcast-data|position</cache-name>
<scheme-name>broadcast</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|position-audit</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
<cache-mapping>
<cache-name>broadcast-data|trade</cache-name>
<scheme-name>broadcast-remote</scheme-name>
</cache-mapping>
</caching-scheme-mapping>
</cache-config>
I'm trying to get the inner text of all the cache names, which exist under each cache-mapping node, and put them all in a list. I have this Model.cs class to do that.
class Model
{
private XmlDocument cacheFile = new XmlDocument();
private List<string> cacheNames = new List<string>();
private int nameCount = 0;
public Model()
{
this.loadNames();
}
public void loadNames()
{
try //exception handling
{
cacheFile.Load("../../resources/file.xml");
}
catch (System.IO.FileNotFoundException)
{
Debug.WriteLine("File not found!");
Environment.Exit(1);
}
catch (System.ArgumentException)
{
Debug.WriteLine("Invalid path!");
Environment.Exit(1);
}
catch (Exception e)
{
Debug.WriteLine("Exception thrown!");
Debug.WriteLine(e);
Environment.Exit(1);
}
//get cache names
XmlNodeList nodes = cacheFile.SelectNodes("/cache-config/caching-scheme-mapping/cache-mapping");
foreach (XmlNode node in nodes)
{
string name = node.FirstChild.InnerText;
cacheNames.Add(name);
nameCount++;
}
}
//accessors
public List<string> getCacheNames()
{
return cacheNames;
}
public int getNameCount()
{
return nameCount;
}
}
However, every time I create a Model object and then check if the List was loaded up, it tells me the list is empty! It appears as though the foreach loop never actually runs, or as if the program can't find the nodes I'm specifying. Please help.
If you use LINQ to XML, this is really quite simple:
XNamespace ns = "http://xmlns.oracle.com/coherence/coherence-cache-config";
var doc = XDocument.Load("../../resources/file.xml");
cacheNames = doc.Descendants(ns + "cache-name").Select(e => e.Value).ToList();
You don't need to keep a separate count of items, you can get this from the list:
cacheNames.Count;
As an aside, idiomatic C# uses pascal casing for methods and properties, so if you stuck to this your methods would start with a capital letter - e.g. GetCacheNames.
This has been puzzling many and has been asked many times here in SO. Your XML has default namespace here :
xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
Descendant elements inherit ancestor default namespace, unless otherwise specified (using explicit namespace prefix or having local default namespace that point to different namespace URI). Using XmlDocument you can use XmlNamespaceManager to register prefix to namespace URI mapping, and use the registered prefix properly in your XPath query, for example :
var nsMgr = new XmlNamespaceManager(new NameTable());
nsMgr.AddNamespace("d", "http://xmlns.oracle.com/coherence/coherence-cache-config");
var xpath = "/d:cache-config/d:caching-scheme-mapping/d:cache-mapping";
XmlNodeList nodes = cacheFile.SelectNodes(xpath, nsMgr);
Anyway, if you have just started this, switching to newer class XDocument would be a better option.

How to edit an Xml file's element values

I have this Xml file, and I want to edit any of the elements like homepage or search_provider.
<?xml version="1.0" encoding="utf-8"?>
<Preferences>
<personal>
<homepage>http://duckduckgo.com</homepage>
<search_provider>DuckDuckGo</search_provider>
<search_provider_url>http://duckduckgo.com/?q=</search_provider_url>
</personal>
</Preferences>
The following is the C# code I'm using to attempt to change the homepage element. Let's say I run saveSetting("homepage", "http://google.com");
public static void saveSetting(String settingName, String newvalue)
{
XmlDocument xml = new XmlDocument();
xml.Load(userSettingsFile);
foreach (XmlElement element in xml.SelectNodes("Preferences"))
{
foreach (XmlElement oldsettingname in element)
{
element.SelectSingleNode(settingName);
XmlNode settingtosave = xml.CreateElement(settingName);
settingtosave.InnerText = newvalue;
element.ReplaceChild(settingtosave, oldsettingname);
xml.Save(userSettingsFile);
}
}
}
Now, while this works to an extent and does change the specified value, it also deletes the entire personal element.
<?xml version="1.0" encoding="utf-8"?>
<Preferences>
<homepage>http://google.com</homepage>
</Preferences>
Hopefully someone can help me out! I've been searching for the last two days for a solution and this is the closest I've come to getting the code to work the way I need it to.
You can just use LINQ to XML like this:
public static void saveSetting(String settingName, String newvalue)
{
var xmlDocument = XDocument.Load("path");
var element = xmlDocument.Descendants(settingName).FirstOrDefault();
if (element != null) element.Value = newvalue;
xmlDocument.Save("path");
}
See this documentation for more details: Modifying Elements, Attributes, and Nodes in an XML Tree
keep your current code and just modify a few lines:
public static void saveSetting(String settingName, String newvalue)
{
XmlDocument xml = new XmlDocument();
xml.Load(userSettingsFile);
foreach (XmlElement element in xml.SelectNodes("Preferences"))
{
if(element.Name.Equals(settingName))
{
element.InnerText = newvalue;
break;
}
}
xml.Save(userSettingsFile);
}

Deserializing child nodes outside of parent's namespace using XmlSerializer.Deserialize() in C#

I have an application that uses namespaces to help deserialize objects stored in XML. The XML namespace is also the C# namespace where the object resides. For example, given the following XML snip:
<xml-config xmlns:app="MyAppNS">
<app:Person>
<Name>Bill</Name>
<Car>
<Make>Honda</Make>
<Model>Accord</Model>
</Car>
</app:Person>
<app:Person>
<Name>Jane</Name>
<Car>
<Make>VW</Make>
<Model>Jetta</Model>
</Car>
</app:Person>
<app:Car>
<Make>Audi</Make>
<Model>A6</Model>
</app:Car>
</xml-config>
The configuration is really just a random bag of objects. As you can see, there is a mix of Person and Car objects at the top level. I use the namespace to determine the object type at load time for proper deserialization. Here is the code for loading the document:
public static void LoadFile(String file) {
XmlDocument doc = new XmlDocument();
doc.Load(file);
XmlNode root = doc.DocumentElement;
foreach (XmlNode child in root.ChildNodes) {
if (child.NodeType != XmlNodeType.Element) {
continue;
}
Object obj = LoadObject(child);
// TODO handle object here...
}
}
private static Object LoadObject(XmlNode node) {
Object obj = null;
StringBuilder str = new StringBuilder();
if ((node.Prefix != null) && (node.Prefix != "")) {
str.Append(node.NamespaceURI).Append('.');
}
str.Append(node.LocalName);
Assembly assy = Assembly.GetExecutingAssembly();
Type type = assy.GetType(str.ToString());
XmlSerializer serdes = new XmlSerializer(type);
using (XmlNodeReader reader = new XmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
return obj;
}
You many see the issue right away, but when this code is used, the resulting Person objects are empty, however there are no errors. The only way to get the deserializer to populate the child elements is to use the same namespace for the child elements:
<app:Person>
<app:Name>Bill</app:Name>
<app:Car>
<app:Make>Honda</app:Make>
<app:Model>Accord</app:Model>
</app:Car>
</app:Person>
The only other option I have tried is to use fully-qualified type names as the element name (no namespaces), however I haven't had any luck with that. Is there a way to cause the deserializer to accept the non-prefixed children of the XML node?
I finally found a method to accomplish this, modifying the answer found here.
I created a custom deserializer that replicates the namespace for the starting node:
public class CustomXmlNodeReader : XmlNodeReader {
private String _namespace;
public CustomXmlNodeReader(XmlNode node) : base(node) {
_namespace = node.NamespaceURI;
}
public override String NamespaceURI {
get { return _namespace; }
}
}
Using this reader, I am able to load stored objects with the following:
using (XmlNodeReader reader = new CustomXmlNodeReader(node)) {
obj = serdes.Deserialize(reader);
}
I know this is a bit bad form for XML (forcing the application of a specific namespace), however it suits my needs quite nicely. Answered here in case it helps anyone else.

Categories

Resources