Find Xml attribute by value, and change its other value - c#

So, below I made a code which lists my 2 items in comboBox1 by its name(Selena and Maria) on load, and when I select one of those, lets say Maria, and click on button1, it populates my 3 textboxes with Maria's name,usn and pawd attribute values, and it looks like:
Display name: Maria
Username: mary26
Password: d4e5r
and I am happy with that part of code, because it serves my purpose.
But I am struggling with part of code which I am trying to figure out.
I created a button2, and I would like that, when I change values of Display name, Username or Password textboxes, and I click save, that it saves to right location in xml file, to Maria, and does not save it to Selena or something else.
I have tried browsing for a week now, and multiple solutions, and I couldn't find any.
att.xml:
<database>
<item name="Selena" usn="sele22" pawd="fed47a"></item>
<item name="Maria" usn="mary26" pawd="d4e5r"></item>
<database>
myproject:
private void Form3_Load(object sender, EventArgs e)
{
comboBox1.Items.Clear();
XmlTextReader reader = new XmlTextReader("att.xml");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == "item")
{
comboBox1.Items.Add(reader.GetAttribute("name"));
}
}
}
reader.Close();
}
private void button1_Click(object sender, EventArgs e)
{
string secit = comboBox1.SelectedItem as string;
XmlTextReader lola = new XmlTextReader("att.xml");
while (lola.Read())
{
if (lola.NodeType == XmlNodeType.Element)
{
string poop = lola.GetAttribute("name");
if (poop == secit)
{
string username = lola.GetAttribute("usn");
string password = lola.GetAttribute("pawd");
string dispname = lola.GetAttribute("name");
textBox1.Text = dispname;
textBox2.Text = username;
textBox3.Text = password;
}
}
}
lola.Close();
}

You can use XmlDocument like this:
XmlDocument doc = new XmlDocument();
doc.Load("att.xml");
foreach(XmlNode item in doc.SelectNodes("//item"))
comboBox1.Items.Add(item.Attributes["name"].Value);
void button3_Click(object sender, NotifyArgs e)
{
XmlNode item = doc.SelectSingleNode("//item[#name='" + comboBox1.Text + "']");
if (item == null) return;
item.Attributes["name"].Value = textBox1.Text;
...
doc.Save("att.xml");
}

Another alternative would be to use XDocument, if you care to learn LINQ.
Lets assume you have a separate method to update the XML file, perhaps it looks like this.
private static void Update(string key, string pwd, string usn)
{
// Enter code here to update the item
}
You can use XPath to find an element with a name:
var document = XDocument.Load("XMLFile1.xml");
var element = document.XPathSelectElement(string.Format("/database/item[#name = \"{0}\"]", key));
if (element != null)
{
element.SetAttributeValue("usn", usn);
element.SetAttributeValue("pawd", pwd);
document.Save("XMLFile2.xml");
}
Or by finding the document using XDocument/XElement/LINQ methods:
var document = XDocument.Load("XMLFile1.xml");
var element = document.Elements("database")
.Elements("item")
.Attributes("name")
.Where(a => a.Value == key)
.Select(a => a.Parent)
.SingleOrDefault();
if (element != null)
{
element.SetAttributeValue("usn", usn);
element.SetAttributeValue("pawd", pwd);
document.Save("XMLFile3.xml");
}
Or you could rewrite it as a LINQ expression.
var document = XDocument.Load("XMLFile1.xml");
var elements = from e1 in document.Elements()
where e1.Name == "database"
from e2 in e1.Elements()
where e2.Name == "item"
from attribute in e2.Attributes()
where attribute.Name == "name" && attribute.Value == key
select e2;
var element = elements.SingleOrDefault();
if (element != null)
{
element.SetAttributeValue("usn", usn);
element.SetAttributeValue("pawd", pwd);
document.Save("XMLFile3.xml");
}
Feel free to adapt accordingly.

Related

How do I change the value of a specific element when all the elements are the same?

Sorry to post another question the next day but im really stumped on how to get this to work. So in my program I got a form that when you click submit it's sopposed to change a specific element. But it gives an error "'lEnumerable' does not contain a definition for 'SetElementValue'"
Can anyone help please? Here is my code.
private void SubmitBtn_Click(object sender, EventArgs e)
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string dataPath = Path.Combine(appDataPath, "Remembrall 2.0");
string EventslistFullPath = Path.Combine(dataPath, "Events.xml");
string[] Elementlist = new string[] { };
Elementlist = XDocument.Load(EventslistFullPath).Descendants("Events").Elements("Event").Select(element => element.Value).ToArray();
XElement EventDoc = XElement.Load(EventslistFullPath);
var Eventselect = EventDoc.Descendants("Events"). Elements("Event").Where(eve => eve.Value == MainForm.ModDisc);
if (string.IsNullOrEmpty(ModifyTexbox.Text) || Elementlist.Contains(ModifyTexbox.Text))
{
MessageBox.Show("Must be unique and have actual content.");
this.DialogResult = DialogResult.None;
}
else
Eventselect.SetElementValue("Event", ModifyTexbox.Text);
EventDoc.Save(EventslistFullPath);
this.DialogResult = DialogResult.OK;
}
The xml looks like this
<Events>
<Event>This is an example</Event>
<Event>This is another example</Event>
</Events>
Change the line:
var Eventselect = EventDoc.Descendants("Events"). Elements("Event").Where(eve => eve.Value == MainForm.ModDisc);
to:
var Eventselect = EventDoc.Descendants("Events"). Elements("Event").Where(eve => eve.Value == MainForm.ModDisc).Single();
or to:
var Eventselect = EventDoc.Descendants("Events"). Elements("Event").Single(eve => eve.Value == MainForm.ModDisc);
you need the variable to contain a single element for SetElementValue(...) to work. Where(...) without Single(...) returns an Enumerable of several elements

XML element with multiple different Text elements

I have the following elements as part of an XML document:
<RegisterEntry>
<EntryNumber>3</EntryNumber>
<EntryDate>2009-01-30</EntryDate>
<EntryType>Registered Charges</EntryType>
<EntryText>REGISTERED CHARGE dated 30 December 2008.</EntryText>
</RegisterEntry>
<RegisterEntry>
<EntryNumber>4</EntryNumber>
<EntryType>Registered Charges</EntryType>
<EntryText>REGISTERED CHARGE dated 30 December 2008.</EntryText>
</RegisterEntry>
I am using XmlReader to iterate through the document. The RegisterEntry is an XMLNodeType.Element and the four enclosed in this element are XmlNodeType.Text. How can I assign each of these Text values to a different variable as the XmlReader returns an empty string for Node.Name on a NodeType.Text. Also, the repeated elements do not always have the same number of text elements. Code below:
XmlTextReader reader = new XmlTextReader(fName);
if(reader.NodeType == XmlNodeType.Element && reader.Name =="RegisterEntry")
{
propEntryNo = "";
propEntryDate = "";
propEntryType = "";
propEntryText = "";
while(reader.Read())
{
if(reader.NodeType == XmlNodeType.Text && reader.Name == "EntryNumber" && reader.HasValue)
{
propEntryNo = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryDate" && reader.HasValue)
{
propEntryDate = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryType" && reader.HasValue)
{
propEntryType = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryText" && reader.HasValue)
{
propEntryText += reader.Value + ",";
}
if(reader.NodeType == XmlNodeType.EndElement && reader.Name == "RegisterEntry")
{
add variable values to list
break;
}
}
}
In each of the if statements above the NodeType returns as Text and the Name as an empty string.
The XML element and the text inside are different nodes!
You have to read the content of the XML element first. Simple example:
switch (reader.Name)
{
// found a node with name = "EntryNumber" (type = Element)
case "EntryNumber":
// make sure it's not the closing tag
if (reader.IsStartElement())
{
// read the text inside the element, which is a seperate node (type = Text)
reader.Read();
// get the value of the text node
propEntryNo = reader.Value;
}
break;
// ...
}
Another option would be ReadElementContentAsString
switch (reader.Name)
{
case "EntryNumber":
propEntryNo = reader.ReadElementContentAsString();
break;
// ...
}
Of course, these simple examples assume that the XML is in the expected format. You should include appropriate checks in your code.
As for the other suggested solutions:
You could XmlDocument or XDocument or instead. The handling is easier, but the memory overhead is bigger (see also).
Deserializing the XML into objects is another option. But I feel handling errors caused by an unexpected format is trickier then.
You can use XDocument to list your RegisterEntry child node like
class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\XMLFile14.xml");
var registerEntries = doc.Descendants("RegisterEntry");
var result = (from e in registerEntries
select new
{
EntryNumber = e.Element("EntryNumber") != null ? Convert.ToInt32(e.Element("EntryNumber").Value) : 0,
EntryDate = e.Element("EntryDate") != null ? Convert.ToDateTime(e.Element("EntryDate").Value) : (DateTime?)null,
EntryType = e.Element("EntryType") != null ? e.Element("EntryType").Value : "",
EntryText = e.Element("EntryText") != null ? e.Element("EntryText").Value : "",
}).ToList();
foreach (var entry in result)
{
Console.WriteLine($"EntryNumber: {entry.EntryNumber}");
Console.WriteLine($"EntryDate: {entry.EntryDate}");
Console.WriteLine($"EntryType: {entry.EntryType}");
Console.WriteLine($"EntryText: {entry.EntryText}");
Console.WriteLine();
}
Console.ReadLine();
}
}
Output:
You can also make certain operations on your list like.
//If you want to get all `EntryText` in xml to be comma separated then you can do like
string propEntryText = string.Join(", ", result.Select(x => x.EntryText));
//Get first register entry from xml
var getFirstRegisterEntry = result.FirstOrDefault();
//Get last register entry from xml
var getLastRegisterEntry = result.LastOrDefault();
//Get register entry from xml with specific condition
var getSpecificRegisterEntry = result.Where(x => x.EntryNumber == 3).SingleOrDefault();

Find all the child node of specific value in C#

<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;

Count child nodes of node matching listbox item

Im having a problem here with selected index change. My xml file contains module number, module name, assesments, credits etc. What im trying to achieve here is - some course details are loaded into listbox from xml (module name and code) but when user selects a module, label should display how many assessments that module has.
Here is my XML file sample
<module>
<moduleCode>ECWM618</moduleCode>
<moduleTitle>Semantic and Social Web</moduleTitle>
<credits>15</credits>
<semester>2</semester>
<assessmentDetails>
<assessment>
<assessmentName>Coursework1</assessmentName>
<assessmentType>Coursework</assessmentType>
<assessmentWeighting>25</assessmentWeighting>
</assessment>
<assessment>
<assessmentName>Coursework2</assessmentName>
<assessmentType>Coursework</assessmentType>
<assessmentWeighting>25</assessmentWeighting>
</assessment>
<assessment>
<assessmentName>Exam</assessmentName>
<assessmentType>Exam</assessmentType>
<assessmentWeighting>50</assessmentWeighting>
</assessment>
</assessmentDetails>
</module>
And here is the code i got
private void moduleSummaryBox_SelectedIndexChanged(object sender, EventArgs e)
{
// when module from modulelist is selected, it read all indicies assigned to the module.
//!!!!!!!! it reads last node only :(? wtf im tired ...lol
//
// read data from modulelist
string path = Directory.GetCurrentDirectory();
FileStream fs = new FileStream(#"myCourse.xml", FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read);
XmlReader moduleReader = XmlReader.Create(fs);
moduleReader.Read();
while (moduleReader.Read())
{
int assessmentNo = 0;
bool match = false;
moduleReader.MoveToElement();
if (moduleReader.Name == "assessmentName")
{
moduleReader.Read();// reads xml file.
XmlNodeType nType = moduleReader.NodeType; //XmlNodeType Specifies the type of node. If node matches specified name = true.
if (nType == XmlNodeType.Text)// if node type match XmlNodeType ( and there is some text in it) statement becomes true
{
this.assno.Text = (moduleReader.Value.ToString()); //set mcode label to matched node.
}
}
if (moduleReader.Value.ToString() == moduleSummaryBox.Items[moduleSummaryBox.SelectedIndex].ToString())
{
match = true;
}
if (match == true)
{
break;
}
}
}
Help appreciated thank you :)
EDIT:
This is what goes into listbox
moduleSummaryBox.Items.Clear();
XmlDocument doc = new XmlDocument();
doc.Load(#"myCourse.xml");
XmlNodeList levelList = doc.GetElementsByTagName("level"+l_level);
foreach (XmlNode node in levelList)
{
XmlElement moduleElement = (XmlElement)node;
XmlNodeList modules_individ = moduleElement.GetElementsByTagName("module");
foreach (XmlNode nodes in modules_individ)
{
XmlElement moduleSeperator = (XmlElement)nodes;
string ll_moduleCode = moduleSeperator.GetElementsByTagName("moduleCode")[0].InnerText;
string ll_moduleTitle = moduleSeperator.GetElementsByTagName("moduleTitle")[0].InnerText;
moduleSummaryBox.Items.Add(ll_moduleCode+" : " + ll_moduleTitle+" ");
}
}
Can you Try With the code sample below:-
const string xmlString =
"<module><moduleCode>ECWM618</moduleCode><moduleTitle>Semantic and Social Web</moduleTitle><credits>15</credits>" +
"<semester>2</semester><assessmentDetails><assessment><assessmentName>Coursework1</assessmentName><assessmentType>Coursework</assessmentType>" +
"<assessmentWeighting>25</assessmentWeighting></assessment><assessment><assessmentName>Coursework2</assessmentName><assessmentType>Coursework</assessmentType>" +
"<assessmentWeighting>25</assessmentWeighting></assessment><assessment><assessmentName>Exam</assessmentName><assessmentType>Exam</assessmentType><assessmentWeighting>50</assessmentWeighting></assessment></assessmentDetails></module>";
var xml = XElement.Parse(xmlString);
var qry =
xml.Descendants()
.Where(e => e.Name == "moduleCode" && e.Value == "ECWM618")
.Ancestors()
.Descendants()
.Where(e => e.Name == "assessmentDetails")
.Elements("assessment").Count();

XmlDocument query taking two values

How I can make following query
If I have XmlDocument and it may have following xml
<EquipmentParameterModified dateTime="2011-04-06T12:03:10.00+01:00" parameter="ExtApp">
<Extensions ParameterId="External App Interface" FromParameterValue="" ToParameterValue="DISABLED"/>
</EquipmentParameterModified>
How I can check that I have EquipmentParameterModified and take values of ParameterId and ToParameterValue
Thanks for help.
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(new StringReader(xmlstr));
XmlNode node = xmldoc.GetElementsByTagName("Extensions").Item(0);
string id = node.Attributes["ParameterId"].Value;
string val = node.Attributes["ToParameterValue"].Value;
Are you trying to find the element given the 2 input search values? What do you want your output to be? If you just want to see that you have a matching element, this code should do the trick:
If yes, try something like this:
public static void Main()
{
var paramId = "External App Interface";
var toParameterValue = "DISABLED";
var xdoc = XDocument.Parse(#"
<EquipmentParameterModified dateTime='2011-04-06T12:03:10.00+01:00' parameter='ExtApp'>
<Extensions ParameterId='External App Interface' FromParameterValue='' ToParameterValue='DISABLED'/>
</EquipmentParameterModified>");
var ret = xdoc.Root
.Elements("Extensions")
.Where(e => e.Attribute("ParameterId").Value == paramId &&
e.Attribute("ToParameterValue").Value == toParameterValue)
.FirstOrDefault();
if (ret != null)
Console.WriteLine(ret.Name);
}
Update for .NET 2.0 & XmlDocument:
public static void Main()
{
var paramId = "External App Interface";
var toParameterValue = "DISABLED";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(#"
<EquipmentParameterModified dateTime='2011-04-06T12:03:10.00+01:00' parameter='ExtApp'>
<Extensions ParameterId='External App Interface' FromParameterValue='' ToParameterValue='DISABLED'/>
</EquipmentParameterModified>");
XmlNode node = xmlDoc.GetElementsByTagName("Extensions")[0];
if (node.Attributes["ParameterId"].Value == paramId &&
node.Attributes["ToParameterValue"].Value == toParameterValue)
{
Console.WriteLine("Found matching node:" + node.Name);
return;
}
}
I recommend using XPath to get the element you're aiming for, do a null check, then get specific attributes of that element, doing a null check on the attribute value before calling the .Value property.

Categories

Resources