I'm trying to extend a tree structure based XDocument.
It should be able to add sections and parameter-value combinations as attributes
The depth of the tree is not fixed.
So the function should verify if something exists and if not add it.
<Body>
<SectionA>
<SectionB paramB="valB" paramB2="val2">
<SectionC paramB="valB" paramNEW="NEW">
</SectionC>
<SectionD>
<SectionE>
</SectionE>
</SectionD>
</SectionB>
</SectionA>
</Body>
So i wrote the following
public void Create(string treenode, string parameter, string value, XDocument doc)
{
treenode =treenode.Replace('\\','/');
string[] branch = treenode.Split('/');
string tree = "";
for (int i = 0; i < branch.Length; i++) // walk trough tree
{
tree = tree +"/"+ branch[i];
bool exists = (bool)doc.XPathEvaluate("boolean("+tree+")");
if (exists==false) //...what goes here ?.
}
}
I'm a bit confused of what functions to use after the "what goes here". Because the depth of the tree can vary.
This can be solved using recursion. Try the following:
Entry point
void Entry()
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(#"<Body>
<SectionA>
<SectionB paramB = 'valB' paramB2='val2'>
<SectionC paramB = 'valB' paramNEW='NEW'>
</SectionC>
<SectionD>
<SectionE>
</SectionE>
</SectionD>
</SectionB>
</SectionA>
</Body>");
string treeNode = #"Body\SectionA\SectionB\SectionC\SectionX";
Create(treeNode, "paramX", "valueX", doc);
}
Your existing code is here
public void Create(string treeNode, string parameter, string value, XmlDocument doc)
{
treeNode = treeNode.Replace('\\', '/');
string[] branch = treeNode.Split('/');
string tree = "";
for (int i = 0; i < branch.Length; i++) // walk trough tree
{
tree = tree + "/" + branch[i];
XmlNode node = CreateXPath(doc, tree, parameter, value);
//if (exists == false) //...what goes here ?.
}
}
Recursive functions:
private XmlNode CreateXPath(XmlDocument doc, string xpath, string parameter, string value)
{
return CreateXPath(doc, doc as XmlNode, xpath, parameter, value);
}
private XmlNode CreateXPath(XmlDocument doc, XmlNode parent, string xpath, string parameter, string value)
{
string[] partsOfXPath = xpath.Trim('/').Split('/');
string nextNodeInXPath = partsOfXPath.First();
if (string.IsNullOrEmpty(nextNodeInXPath))
{
if (parent.Attributes[parameter] == null && parent.LocalName == xpath)
{
// Attribute not found, create it
((XmlElement)parent).SetAttribute(parameter, value);
}
return parent;
}
XmlNode node = parent.SelectSingleNode(nextNodeInXPath);
if (node == null)
{
// node not found, create it and additionally add attributes
node = parent.AppendChild(doc.CreateElement(nextNodeInXPath));
((XmlElement)node).SetAttribute(parameter, value);
}
// Call recursive
string rest = String.Join("/", partsOfXPath.Skip(1).ToArray());
return CreateXPath(doc, node, rest, parameter, value);
}
Got the following output:
<?xml version="1.0"?>
<Body>
<SectionA>
<SectionB paramB2="val2" paramB="valB">
<SectionC paramB="valB" paramNEW="NEW">
<SectionX paramX="valueX"/>
</SectionC>
<SectionD>
<SectionE/>
</SectionD>
</SectionB>
</SectionA>
</Body>
Related
For example:
<Inflection>innerText1</Inflection>
<Inflection>innerText2<Variant>innerText2Variant</Variant></Inflection>
We can get the innerText1 in C# by theNode.InnerText property but
how can we get the following info separately for the node in 2nd line?
innerText2
XmlNode of Variant
innerText2Variant
static void Main(string[] args)
{
string strXML = #"<root><Inflection>innerText1</Inflection>
<Inflection>innerText2<Variant>innerText2Variant</Variant></Inflection></root>";
XmlDocument xml = new XmlDocument();
xml.LoadXml(strXML);
XmlNodeList nodesInflection = xml.SelectNodes("/root/Inflection");
foreach(XmlNode n in nodesInflection)
{
if (n.ChildNodes.Count == 1)
{
Console.WriteLine(n.InnerText);
//or
Console.WriteLine(n.ChildNodes[0].InnerText);
}
else
{
Console.WriteLine(n.ChildNodes[0].InnerText);
XmlNode nodeVariant = n.SelectSingleNode("Variant");
//or
//XmlNode nodeVariant = n.ChildNodes[1];
Console.WriteLine(nodeVariant.InnerText);
}
}
Console.WriteLine("*******press any key");
Console.ReadKey();
}
I want to make a funtion to input value (funName) and check XML file attribute (FunName) then output XML file attribute (isEnable) boolean true or false
How can I modify this code?
My XML file
<itema>
<itemb FunName="ABC" isEnable="true"></itemb>
<itemb FunName="DEF" isEnable="false"></itemb>
</itema>
My Code
public bool FunEnable(string funName , string isEnable)
{
bool result = true;
XmlDocument xDL = new XmlDocument();
xDL.Load("C://XMLFile2.xml"); //Load XML file
XmlNode xSingleNode = xDL.SelectSingleNode("//itemb");
XmlAttributeCollection xAT = xSingleNode.Attributes; //read all Node attribute
for (int i = 0; i < xAT.Count; i++)
{
if (xAT.Item(i).Name == "isEnable")
{
Console.WriteLine(xAT.Item(i).Value); //read we want attribute content
}
}
return result;
}
Thanks a lot
Well you can try this :
public static bool FunEnable(string funNam)
{
bool result = true;
XmlDocument xDL = new XmlDocument();
xDL.Load(#"C:/XMLFile2.xml"); //Load XML file
XmlNodeList nodeList = xDL.SelectNodes("//itemb");
foreach (XmlNode node in nodeList)
{
if (node.Attributes["FunName"].Value.Equals(funNam))
{
result = Convert.ToBoolean(node.Attributes["isEnable"].Value);
break;
}
}
Console.WriteLine("with funName = "+ funNam +" isEnable equal to : " + result);
return result;
}
Output
with funName = ABC isEnable equal to : True
This is fairly trivial using LINQ to XML. You can load the document using XDocument.Load and then get your isEnable value like so:
var result = doc.Descendants("itemb")
.Where(e => (string)e.Attribute("FunName") == "ABC")
.Select(e => (bool)e.Attribute("isEnable"))
.Single();
You can see a working demo here: https://dotnetfiddle.net/MYTOl6
var xDoc = XDocument.Load(path);
bool result = (from itemb in xDoc.Descendants("itemb")
where itemb.Attribute("FunName").Value == funcName
select itemb.Attribute("isEnable").Value == "true")
.FirstOrDefault();
Well, I prefer Linq to XML..
Maybe that one works:
public bool FunEnable(string funName, string isEnable)
{
bool result = true;
XDocument xDL = XDocument.Load("C://XMLFile2.xml");
var xSingleNode = from node in xDL.Descendants("itemb")
where node.Attribute("FunName").Value == funName
select node;
if(xSingleNode.Count() > 0)
{
result = xSingleNode.ElementAt(0).Attribute("isEnable").Value == "true";
//If there is at least one node with the given name, result is set to the first nodes "isEnable"-value
}
return result;
}
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 have a xml file that looks like
<Name>AAA</Name>
<Age>23</Age>
<I1>
<Element1>A</Element1>
<Element2>B</Element2>
<Element3>C</Element3>
<\I1>
<I2>
<Element1>AA</Element1>
<Element2>BB</Element2>
<Element3>CC</Element3>
</I2>
I am reading all the values of elements using xmlreader in C# 3.0. But now i have to change by reading only the values within particular start and end tage. For the xml file mentioned above, i need to read <Name>, <Age> by default and then i have a function that returns the value "I1" or "I2" which is basically the element names. If it returns "I1" then i should read only the elements between <I1> and </I1> and should not read <I2> and vice versa. So the code structure would be (just the logic please ignore the syntax errors) like
/******function that returns element name I1 or I2*********/
string elementName = func(a,b);
xmlreader reader = reader.create("test.xml");
while(reader.read())
{
switch(reader.nodetype)
{
case xmlnodetype.element:
string nodeName = reader.name
break;
case xmlnodetype.text
switch(nodeName)
{
/*************Read default name and age values*******/
case "Name":
string personName = reader.value
break;
case "Age":
string personAge = reader.value;
break;
/*******End of reading default values**************/
/*** read only elements between the value returned by function name above
If function returns I1 then i need to read only values between <I1> </I1> else read </I2> and </I2>**/
}
}
}
Thanks!
So assuming, since we dont have any other tags to go off, that your file would look something such as this from beginning to end
<?xml version="1.0" encoding="utf-8" ?>
<RootElement>
<UserInformation>
<Name>AAA</Name>
<Age>23</Age>
<I1>
<Element1>A</Element1>
<Element2>B</Element2>
<Element3>C</Element3>
<\I1>
<I2>
<Element1>AA</Element1>
<Element2>BB</Element2>
<Element3>CC</Element3>
</I2>
</UserInformation>
</RootElement>
and then to call it
System.IO.StreamReader sr = new System.IO.StreamReader("test.xml");
String xmlText = sr.ReadToEnd();
sr.Close();
List<UserInfo> finalList = readXMLDoc(xmlText);
if(finalList != null)
{
//do something
}
private List<UserInfo> readXMLDoc(String fileText)
{
//create a list of Strings to hold our user information
List<UserInfo> userList = new List<UserInfo>();
try
{
//create a XmlDocument Object
XmlDocument xDoc = new XmlDocument();
//load the text of the file into the XmlDocument Object
xDoc.LoadXml(fileText);
//Create a XmlNode object to hold the root node of the XmlDocument
XmlNode rootNode = null;
//get the root element in the xml document
for (int i = 0; i < xDoc.ChildNodes.Count; i++)
{
//check to see if we hit the root element
if (xDoc.ChildNodes[i].Name == "RootElement")
{
//assign the root node
rootNode = xDoc.ChildNodes[i];
break;
}
}
//Loop through each of the child nodes of the root node
for (int j = 0; j < rootNode.ChildNodes.Count; j++)
{
//check for the UserInformation tag
if (rootNode.ChildNodes[j].Name == "UserInformation")
{
//assign the item node
XmlNode userNode = rootNode.ChildNodes[j];
//create userInfo object to hold results
UserInfo userInfo = new UserInfo();
//loop through each if the user tag's elements
foreach (XmlNode subNode in userNode.ChildNodes)
{
//check for the name tag
if (subNode.Name == "Name")
{
userInfo._name = subNode.InnerText;
}
//check for the age tag
if (subNode.Name == "Age")
{
userInfo._age = subNode.InnerText;
}
String tagToLookFor = "CallTheMethodThatReturnsTheCorrectTag";
//check for the tag
if (subNode.Name == tagToLookFor)
{
foreach (XmlNode elementNode in subNode.ChildNodes)
{
//check for the element1 tag
if (elementNode.Name == "Element1")
{
userInfo._element1 = elementNode.InnerText;
}
//check for the element2 tag
if (elementNode.Name == "Element2")
{
userInfo._element2 = elementNode.InnerText;
}
//check for the element3 tag
if (elementNode.Name == "Element3")
{
userInfo._element3 = elementNode.InnerText;
}
}
}
}
//add the userInfo to the list
userList.Add(userInfo);
}
}
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show(e.Message);
return null;
}
//return the list
return userList;
}
//struct to hold information
struct UserInfo
{
public String _name;
public String _age;
public String _element1;
public String _element2;
public String _element3;
}
<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;