I have problem with an editting existing xml file. I was looking for a solution but I can not find solution which I need. Can someone help me please?
Here is my code:
private void referenceToXML(string path)
{
var filePath = path;
var xmlDoc = XDocument.Load(filePath);
var parentElement = new XElement("Items");
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", question.text);
var lastNameElement = new XElement("Cathegory", SM.text);
parentElement.Add(firstNameElement);
firstNameElement.Add(lastNameElement);
var rootElement = xmlDoc.Element("ItemCollection");
rootElement.Add(parentElement);
xmlDoc.Save(path);
}
And here is result of the code:
https://pastebin.com/LKGJER38
But I need this:
https://pastebin.com/RRC75pR8
I will appreciate every help.
Add to the Items Element instead :
private void referenceToXML(string path)
{
var xmlDoc = XDocument.Load(path);
xmlDoc.Element("ItemCollection").Element("Items").Add(
new XElement("Item", new XAttribute("name", question.text), SM.text));
xmlDoc.Save(path);
}
Your problem is that you are unconditionally adding an <Items> element every time you add an <Item> element. Instead, you need to check whether such an element exists, and if so, use it. The following extension method makes that easy:
public static partial class XNodeExtensions
{
public static XElement GetOrAddElement(this XContainer container, XName name)
{
if (container == null || name == null)
throw new ArgumentNullException();
var element = container.Element(name);
if (element == null)
container.Add(element = new XElement(name));
return element;
}
}
Now you can modify your referenceToXML method to use it as follows:
private static void referenceToXML(string path, string questionText, string smText)
{
AddItem(XDocument.Load(path), questionText, smText).Save(path);
}
static XDocument AddItem(XDocument doc, string questionText, string smText)
{
var firstNameElement = new XElement("Item");
firstNameElement.SetAttributeValue("name", questionText);
var lastNameElement = new XElement("Cathegory", smText);
firstNameElement.Add(lastNameElement);
// Get or create the root element ItemCollection
var root = doc.GetOrAddElement("ItemCollection");
// Get or create the Items list
var items = root.GetOrAddElement("Items");
// Add the item
items.Add(firstNameElement);
return doc;
}
Demo fiddle here.
Note I modified your code to make question.text and SM.text arguments instead of class members for clarity and testing purposes.
Related
The XML bills usually have one Node that is returned and parsed. We have come across an issue where an XML bill had multiple Nodes. Since the code is not set up to handle that, the customer ended up with an incorrect bill.
This is the code I have that goes through the bill list. If it comes back with a node then it parses the information from the xml.
var response = new List<CustomerBill>();
try
{
foreach (GetBillForCAResponse eBillResponse in eBillResponseList)
{
var statementDetailsResponse = GetStatementDetails(
new GetStatementDetailsRequest
{
BatchId = eBillResponse.BatchId,
CustomerAccountId = eBillResponse.CA.ToString("000000000"),
StatementId = eBillResponse.CAS_NUM.ToString("0000")
});
string xmlBill = statementDetailsResponse.StatementAsXML.ToString();
var document = new XmlDocument();
document.LoadXml(xmlBill);
var saDetailedPageNode = XmlBillParser.GetDetailPageSectionBySa(requestSa, xmlBill);
if (saDetailedPageNode == null) continue;
var customerBill = new CustomerBill();
customerBill.IsSurepay = XmlBillParser.GetSurepayFlagFromBill(document);
customerBill.ServiceAddress = XmlBillParser.GetServiceAddress(requestSa, document);
customerBill.monthName = XmlBillParser.GetnillStatementDate(requestSa, xmlBill);
customerBill.EqCurlPlanBal = XmlBillParser.getEqualizerCurrentPlanBalance(document);
customerBill.EqPymntDue = XmlBillParser.getEqualizerPaymentDue(document);
customerBill.Service = GetServiceAccountUsageAndBillingDetail(requestSa, xmlBill, saDetailedPageNode);
response.Add(customerBill);
}
}
catch (Exception ex)
{
trace.Write(new InvalidOperationException(requestSa, ex));
}
return response;
}
Here is the method that checks if there is a node in the XML. I have changed the code so that is returns all the nodes. I had to change the type xmlNode to xmlNodeList because now its returning a collection of nodes. ****This is whats causing all the problems in my code in other places.***
public static xmlNode GetDetailPageSectionBySa(string sa, string statementXml)
{
XmlDocument document = new XmlDocument();
document.LoadXml(statementXml);
string requestSa = sa.PadLeft(9, '0');
string xpath = String.Format("//Para[IRBILGP_SA_SAA_ID_PRINT.SERVICE.ACCOUNT.STATMENT='{0}S{1}']/..", requestSa.Substring(0, 4), requestSa.Substring(4));
return document.SelectNodes(xpath);
//var nodes = document.SelectNodes(xpath);
// if(nodes.Count > 0) return nodes[nodes.Count - 1];
//if(!SaExistInBill(requestSa, statementXml)) return null;
//var node = GetDetailPageSectionByBillPrisminfoIndex(sa, statementXml);
//if (node != null) return node;
//return null;
}
So returning back to where it is called.. im getting an invalid arguement here
customerBill.Service = GetServiceAccountUsageAndBillingDetail(requestSA, xmlBill, saDetailedPageNode); because the parameter saDetailedPageNode is xmlNodeList now when it is expecting of type xmlNode. if i go to the method private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNode detailPageNode) which i added at the end of this code so you could see. If I change the parameter XmlNode detailPageNode to XmlNodeList detailPageNode which I have to do to fix the invalid arguement above, I get that detailPageNode.SelectNodes becomes invalid because xmlNodeList does not have SelectNodes as an extension method. I use this extention method a lot through this method. So I am getting a lot of errors.
var saDetailedPageNode = XmlBillParser.GetDetailPageSectionBySa(requestSa, xmlBill);
if (saDetailedPageNode == null) continue;
var customerBill = new CustomerBill();
customerBill.IsSurepay = XmlBillParser.GetSurepayFlagFromBill(document);
customerBill.ServiceAddress = XmlBillParser.GetServiceAddress(requestSa, document);
customerBill.monthName = XmlBillParser.GetnillStatementDate(requestSa, xmlBill);
customerBill.EqCurlPlanBal = XmlBillParser.getEqualizerCurrentPlanBalance(document);
customerBill.EqPymntDue = XmlBillParser.getEqualizerPaymentDue(document);
customerBill.Service = GetServiceAccountUsageAndBillingDetail(requestSa, xmlBill, saDetailedPageNode);
response.Add(customerBill);
}
}
catch (Exception ex)
{
trace.Write(new InvalidOperationException(requestSa, ex));
}
return response;
}
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(string requestSA, string xmlBill, XmlNode detailPageNode)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
var meterReadEndXMLNodes = detailPageNode.SelectNodes("Usage_kWh_b");
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("Usage_kWh_a");
}
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("APSElec_kWh_b");
}
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("APSElec_kWh_a");
}
var demandXMLNodes = detailPageNode.SelectNodes("Usage_kW_Total_Bold");
How can i fix my invalid arguments so that I can use xmlNodeList instead of xmlNode? is there a way to convert or cast? or is there another xml object I can use?
Since I am now returning multiple nodes. I know that I will need to loop through the customerBill portion. How can I do that without creating a new bill for every node? All the nodes in one xml need to be included in one bill.
From the code you've provided, the only part that needs to account for your refactor to XmlNodeList is GetServiceAccountUsageAndBillingDetail(), so you have two options:
Update GetServiceAccountUsageAndBillingDetail() to take in an XmlNodeList parameter instead and aggregate values inside that method to come up with your final ServiceAddressBillDetail object.
Loop through your XmlNode objects in the XmlNodeList and reconcile the different ServiceAddressBillDetail objects yourself.
Without details on what ServiceAddressBillDetail, I can only guess as to which is better, but I'd suggest using the first option.
private static ServiceAddressBillDetail GetServiceAccountUsageAndBillingDetail(
string requestSA,
string xmlBill,
XmlNodeList detailPageNodes)
{
var saBillDetail = new ServiceAddressBillDetail();
saBillDetail.UsageServiceName = requestSA;
foreach(XmlNode detailPageNode in detailPageNodes)
{
var meterReadEndXMLNodes = detailPageNode.SelectNodes("Usage_kWh_b");
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("Usage_kWh_a");
}
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("APSElec_kWh_b");
}
if (meterReadEndXMLNodes.Count == 0)
{
meterReadEndXMLNodes = detailPageNode.SelectNodes("APSElec_kWh_a");
}
var demandXMLNodes = detailPageNode.SelectNodes("Usage_kW_Total_Bold");
//Whatever comes next
}
}
Assuming ServiceAddressBillDetail has properties for "usage" you could simply add the appropriate values from each detailPageNode to the saBillDetail object.
It's hard to see what is going on but this may be the problem;
string xpath = String.Format("//Para..
The "//" will search in the entire document, but you probably want to search for descendent elements.
Here's some code to deal with a similar problem;
XmlNodeList staffNodes = resultXML.SelectNodes("//staff");
List<TempStaff> tempStaffs = staffNodes
.Cast<XmlNode>()
.Select(
i =>
new TempStaff()
{
StaffId = i.SelectSingleNode("id").InnerText,
Forename = i.SelectSingleNode("forename").InnerText,
Surname = i.SelectSingleNode("surname").InnerText,
}
).ToList();
My XML looks like
<staff><forename>asdf</forename><surname>ddsf</surname><id>123</id>... </staff>
<staff><forename>asdfas</forename><surname>asffdf</surname><id>456</id>...</staff>
I'm trying to make a Windows RT program and I can't seem to figure out how to get the value of the root element. The xmldocument only contains:
<double>0.7423</double>
How would I go about getting the value "0.7422" using c# and window store? Every time I try something it returns a null value.
This is what I've tried so far:
`var getRate = from query in xmlDoc.Descendants("double")
select new
{
Rate = query.Value
};
foreach (var query in getRate)
{
rate = Convert.ToDouble(query.Rate);
}`
I also tried this:
`var rate= xmlDoc.Root.Element("double").Value;
var rate= xmlDoc.Element("double").Value;
rate = (double)XElement.Load(xmlstream);`
But rate always returns a null value.
Try this
string xml = "<double>0.7423</double>";
var document = XDocument.Parse(xml);
var doubleValue = document.Descendants("double").FirstOrDefault().Value;
You can access root element of document via Root property:
double d = (double)XDocument.Load(path_to_xml).Root;
But in this case you even don't need to create document. You can create element:
double d = (double)XElement.Load(path_to_xml);
Not tested, but it's the good way I think
XmlDocument doc = new XlmDocument();
doc.Load("path");
XmlNode root = doc.DocumentElement.InnerText
Quite obvious:
internal class Program
{
private static void Main(string[] args)
{
var xml = "<double>0.7423</double>";
Debug.WriteLine("Method1: {0}", Method1(xml));
Debug.WriteLine("Method2: {0}", Method2(xml));
Debug.WriteLine("Method3: {0}", Method3(xml));
}
private static double Method1(string xml)
{
var xdoc = XDocument.Parse(xml);
var doubleStr = xdoc.Root.Value;
var doubleValue = double.Parse(doubleStr, CultureInfo.InvariantCulture);
return doubleValue;
}
private static double Method2(string xml)
{
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml);
return double.Parse(xmlDoc.FirstChild.InnerText, CultureInfo.InvariantCulture);
}
private static double Method3(string xml)
{
var doubleStr = xml.Substring(
xml.IndexOf(">") + 1,
xml.IndexOf("</") - xml.IndexOf(">") - 1
);
return double.Parse(doubleStr, CultureInfo.InvariantCulture);
}
}
Took me a while but it was simpler than I thought. Here's how I did it:
var xelement = XElement.Parse(outputtext);
rate = (double)xelement;
Thank you everyone for your help/suggestions!
I write console application program
this is my xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Settings>
<AsteriskHost type="string">172.16.18.14</AsteriskHost>
</Settings>
I run this code
public void Set(List<AcmSettings> acmSettings)
{
XElement xelement = XElement.Load("Settings.xml");
IEnumerable<XElement> settings = xelement.Elements();
foreach (var item in acmSettings)
{
settings.FirstOrDefault(x => x.Name == item.Name).SetValue("treeee");
}
xelement.Save("Settings.xml");
}
this my test:
[Test]
public void SetShouldUpdateValue()
{
var settingsManager = new SettingsManager();
const string newIp = "165.166.167.167";
const string elemntName = "AsteriskHost";
var acmSetting = new List<AcmSettings> { new AcmSettings { Name = elemntName, Value = newIp } };
settingsManager.Set(acmSetting);
var setting = settingsManager.Get(x => x.Name == elemntName).FirstOrDefault();
Assert.IsTrue(setting != null);
Assert.IsTrue(setting.Value== newIp);
}
I don't have any error but my new value not save in file.
How can I update xml node in c#
I tried your Set method, and it runs ok for me. I did have to tweak your current implementation by changing just one line:
// settings.FirstOrDefault(x => x.Name == item.Name).SetValue("treeee");
settings.FirstOrDefault(x => x.Name == item.Name).SetValue(item.Value);
Perhaps the problem is with your Get method. Here is a quick (not production quality) Get implementation. Using this implementation, your unit test runs successfully.
// note - just a string (name) passed in
public XElement Get(string name)
{
XElement xelement = XElement.Load("Settings.xml");
IEnumerable<XElement> settings = xelement.Elements();
return settings.FirstOrDefault(x => x.Name == name);
}
The other possibility is that, as suggested in the comments above, is that you are looking at your project XML file, and not looking at the output XML file.
Try like this
XmlDocument xmlDom = new XmlDocument();
xmlDom.Load("YourXMLFILEPATH.xml");
XmlNode newXMLNode = xmlDom.SelectSingleNode("/Settings/AsteriskHost");
newXMLNode.InnerText = YourValue;
xmlDom.Save("YourXMLFILEPATH.xml");
Console.WriteLine(xmlDom);
Have you tried this ?
You can modify your set method like this.
public void Set(List<AcmSettings> acmSettings)
{
XElement xelement = XElement.Load("Settings.xml");
IEnumerable<XElement> settings = xelement.Elements();
foreach (var item in acmSettings)
{
xelement.Descendants(item.Name).FirstOrDefault().Value = item.Value;
}
xelement.Save("Settings.xml");
}
<main>
<myself>
<pid>1</pid>
<name>abc</name>
</myself>
<myself>
<pid>2</pid>
<name>efg</name>
</myself>
</main>
that is my XML file named simpan. I have two button. next and previous. What i want to do is, all the info will shows off on the TextBox when the user click the button. The searching node will be based on the pid.
Next button will adding 1 value of pid (let's say pid=2) and it will search on the node that have the same value of pid=2. it also will show the name for the pid=2. (showing name=abc)
Same goes to the previous button where it will reduce 1value of pid (pid=1).
Does anybody knows how to do this?
//-------------EDIT------------------
thanks to L.B, im trying to use his code. however i got an error.
is my implementation of code correct?
private void previousList_Click(object sender, EventArgs e)
{
pid = 14;
XDocument xDoc = XDocument.Parse("C:\\Users\\HDAdmin\\Documents\\Fatty\\SliceEngine\\SliceEngine\\bin\\Debug\\simpan.xml");
var name = xDoc.Descendants("myself")
.First(m => (int)m.Element("PatientID") == pid)
.Value;
textETA.Text = name;
////////////////////
}
int pid = 2;
XDocument xDoc = XDocument.Parse(xml); //Load
var name = xDoc.Descendants("myself")
.First(m => (int)m.Element("pid") == pid)
.Element("name")
.Value;
You can use the following XPath to list all Myself tags, then look for what you want using a simple Linq command:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + "file1.xml");
var resNodes = xmlDoc.SelectNodes("//myself");
XmlNode res = null;
var val = textBox1.Text;
var item = from XmlNode x in resNodes
select x;
foreach (var nodP in item) {
foreach (XmlNode nod in nodP.ChildNodes) {
if (nod.InnerText == val) {
res = nodP;
}
}
}
if (res == null)
// not found!
;
else
// show the result
;
Call me old fashioned but you could use an XPath, for example:
string xml =
#"<main>
<myself>
<pid>1</pid>
<name>abc</name>
</myself>
<myself>
<pid>2</pid>
<name>efg</name>
</myself>
</main>";
using System.Xml;
....
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
// Replace "2" in the string below with the desired pid
XmlNode xmlNode =
xmlDocument.DocumentElement.SelectSingleNode("myself/name[../pid=2]");
// xmlNode contains the <name>efg</name> XmlElement. For example:
string name = xmlNode.Value;
If it can match multiple nodes, for example there could be multiple <myself> elements with a child element <pid> set to 2, use the following instead:
foreach(XmlNode xmlNode in
xmlDocument.DocumentElement.SelectNodes("myself/name[../pid=2]"))
{
// xmlNode contains the matching <name> element
}
In both cases, the value can be extracted from the XmlNode using the Value property.
public class simpman
{
private static XElement root = XElement.Load("Simpman.xml");
public static string GetItem(int index)
{
XElement item =
(from element in root.Elements("myself")
where (int)element.Element("pid") == index
select element.Element("name")).SingleOrDefault();
return item != null ? item.Value : "Please check the Index";
}
}
Initialize a static itemIndex to 1 and use it further like itemIndex++ (for Next) and itemIndex-- (for Prev).
private void previousList_Click(object sender, EventArgs e)
{
pid = 14;
XDocument xDoc = XDocument.Load(#"C:\Users\HDAdmin\Documents\Fatty\SliceEngine\SliceEngine\bin\Debug\simpan.xml");
var name = xDoc.Root
.Descendants("myself")
.FirstOrDefault(e => e.Element("pid")
.Value
.Equals(pid.ToString(CultureInfo.InvariantCulture)))
.Element("name")
.Value;
textETA.Text = name;
}
XmlDocument doc = new XmlDocument();
FileStream fs = new FileStream(rootXMLPath, FileMode.Open, FileAccess.Read);
doc.Load(fs);
XmlNode node = doc.DocumentElement;
nodeName = "/main/myself";
var child1 = node.SelectSingleNode(nodeName).ChildNodes[0].FirstChild.InnerXml;
var child2 = node.SelectSingleNode(nodeName).ChildNodes[0].LastChild.InnerXml;
var child3 = node.SelectSingleNode(nodeName).ChildNodes[1].FirstChild.InnerXml;
var child4 = node.SelectSingleNode(nodeName).ChildNodes[1].LastChild.InnerXml;
I want to only compare the nodes of two xml files but not the value of the nodes using c#.
If the node format in two files are not same then it should pop-up a message..
I will use Linq to XML :
XDocument doc = XDocument.Parse(data);
var list = doc.DescendantNodes().Where(i => i is XElement);
and then use this to compare :
foreach (var item in list)
{
if (((XElement)item).Name.LocalName == propert.Name)
}
but your final implementation should check number of nodes and other issues
XDocument file1 = XDocument.Load("somefile1.xml");
XDocument file2 = XDocument.Load("somefile2.xml");
if (file1.Nodes().Intersect(file2.Nodes()).Count() > 0)
MessageBox.Show("hey i popped up");
Hope this helps...
Assuming that by "node format" you mean elements & their names, this will walk the element tree and compare the names:
void Main()
{
XElement thing = new XElement("test", new XElement("child") );
XElement otherThing = new XElement("test", new XElement("child") );
var comparer = new XElementComparer();
var areSame = comparer.Equals(thing, otherThing);
Console.WriteLine(areSame);
}
class XElementComparer : IEqualityComparer<XElement>
{
public bool Equals(XElement first, XElement second)
{
if (first.Name != second.Name)
return false;
else if (!first.Elements().SequenceEqual(second.Elements(), this))
return false;
else
return true;
}
public int GetHashCode(XElement element) { return element.GetHashCode(); }
}