So, I have a problem to modify my XML-document. Here is my XML
<CookBook>
<Recipe id="1">
<Title>Best recipe</Title>
<Category>...</Category>
<Description>Some text</Description>
<Amount>10</Amount>
<Ingredient>
<li>Ingredient 1</li>
<li>Ingredient 2</li>
</Ingredient>
<RecipeText>
<li>Step 1</li>
<li>Step 2</li>
<li>Step 3</li>
</RecipeText>
</Recipe>
<Recipe id="2">
<Title>Best recipe2</Title>
<Category>...</Category>
<Description>Some text</Description>
<Amount>10</Amount>
<Ingredient>
<li>Ingredient 1</li>
<li>Ingredient 2</li>
<li>Ingredient 3</li>
</Ingredient>
<RecipeText>
<li>heat the oven</li>
<li>Do something</li>
<li>Do something</li>
</RecipeText>
</Recipe>
</CookBook>
So I need to update specific recipe’s li elements which are inside of Ingredient element. But I really don't know how... I have an ingredients list, which contains values that I want to my XML.
foreach (var item in lvAddIngredient.Items)
{
string text = item.ToString();
ingredients.Add(text);
}
When I create a new Recipe I use this code
var doc = XDocument.Load("recipeXML.xml");
var newElement = new XElement("Recipe", new XAttribute("id", id.ToString()),
new XElement("Title", txtTitle.Text),
new XElement("Category", selectedCategory.ToString()),
new XElement("Description", txtDescription.Text),
new XElement("Amount", txtAmount.Text),
new XElement("Ingredient", ingredients.Select(text => new XElement("li", text))),
new XElement("RecipeText", recipeText.Select(text => new XElement("li", text))));
doc.Element("CookBook").Add(newElement);
doc.Save("recipeXML.xml");
But I don't know how to update these li elements value. I have tried something like this, but the syntax is wrong.
var xdoc = XDocument.Load("recipeXML.xml");
string id = lbRecipes.SelectedValue.ToString();
var items = from item in xdoc.Descendants("Recipe")
where item.Attribute("id").Value == id
select item;
foreach (XElement ele in items)
{
ele.SetElementValue("Title", txtTitle.Text);
ele.SetElementValue("Category",cbAddCategory.Text);
ele.SetElementValue("Amount", txtAmount.Text);
ele.SetElementValue("Description", txtDescription.Text);
ele.SetElementValue("Ingredient", ingredients.Select(text => ele.SetElementValue("li", text)));
}
Just use your row identifier "li" to get what you need with your loop.
I suggest you to create a tree view for the XML, with the help of the tree view you can browse through the elements existing in the XML, select any value that you would like to update, serialize the tree view after updating the value and save it back to the XML file.
You can create treeview with the help of this snippet!
try
{
treeView1.Nodes.Clear();
treeView1.Nodes.Add(new TreeNode(doc.DocumentElement.Name));
TreeNode tNode = new TreeNode();
tNode = treeView1.Nodes[0];
AddNode(doc.DocumentElement, tNode);
treeView1.Show();
}
catch (XmlException xmlEx)
{
MessageBox.Show(xmlEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
XmlNode xNode;
TreeNode tNode;
XmlNodeList nodeList;
int i;
if (inXmlNode.HasChildNodes)
{
nodeList = inXmlNode.ChildNodes;
for (i = 0; i <= nodeList.Count - 1; i++)
{
xNode = inXmlNode.ChildNodes[i];
inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
tNode = inTreeNode.Nodes[i];
AddNode(xNode, tNode);
}
}
else
{
inTreeNode.Text = (inXmlNode.InnerText).Trim();
}
Related
i'm trying to read xml file and update all it's value my xml was
<adf>
<prospect>
<requestdate>2015-10-29 07-38-22</requestdate>
<id sequence="1" source="admin.ss.com">admin.ss.com</id>
<vehicle interest="buy" status="">
<id sequence="1" source=""></id>
<year></year>
<make></make>
<model>camry</model>
<vin></vin>
<stock></stock>
<trim></trim>
</vehicle>
<customer>
<contact primarycontact="1">
<name part="first">Jessica</name>
<name part="last">Sonntag</name>
<email>js#test.com</email>
<phone type="phone" time="day">555-585-5555</phone>
<address>
<street line="1"></street>
<city></city>
<regioncode></regioncode>
<postalcode></postalcode>
<country></country>
</address>
</contact>
<comments>Vehicle Year: 2011 Comments: </comments>
</customer>
<provider>
<name part="full">ST</name>
<service> Engine Marketing</service>
<phone>1-866-572-3952</phone>
</provider>
</prospect>
</adf>
so i select node like below
var items = (from item in xmlDoc.Descendants("requestdate")
select item).ToList();
then i can update only requestdata tag value so do i have to repeat same for all tags or is there any good way to accomplish this.
Regards
There is an easy way to do this. This one is a hidden gem. Most people may not know this. This feature came in VS2013 and it's called "Paste XML as Classes."
Save your xml (Ex: MyXml.XML)
Create a new Console project
Open the Xml in Visual studio
Copy All contents of the xml (Ctl+A, Ctl + C)
Add a new class to your project. You can give any name you like.
Go to Edit>Paste Special>Paste XML as classes.
Add another class to your project. Then add below two methods to that class.
public static string Serialise<T>(T serialisableObject)
{
var doc = new XmlDocument();
using (var stream = new StringWriter())
{
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
XmlWriter xmlWriter = XmlWriter.Create(stream, settings);
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(xmlWriter, serialisableObject, ns);
doc.LoadXml(stream.ToString());
}
return doc.InnerXml;
}
public static T Deserialise<T>(string xml)
{
T list;
using (var reader = new StringReader(xml))
{
var serialiser = new XmlSerializer(typeof(T));
list = (T)serialiser.Deserialize(reader);
}
return list;
}
Then in your console applications Main method; add this.
var myObj = new adf();
myObj.prospect = new adfProspect();
myObj.prospect.customer = new adfProspectCustomer(){comments = "dgsrtetetete"};
//populate all fields.....
var xml = MySerializer.Serialise(myObj);
File.WriteAllText(#"C:\myNewXml.xml", xml);
That's it. Same way now you can deserialise an xml object in to your class.
Try the XmlSerializer class: https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer(v=vs.110).aspx If you serialize/deserialize the xml then up dating is trivial.
If you wanted to change every phone number to "0123456789" you could do something like:
var xDoc = XDocument.Load("document.xml");
var results = from phone in xDoc.Descendants("phone") select phone;
foreach (XElement result in results)
{
element.SetValue("0123456789");
}
i have came up with solution with support two extension method i'm iterating all nodes and update.(since my xml is not too big or complicated this one would be a good solution)
with help of these two extension methods
public static void IterateThroughAllNodes(this XmlDocument doc, Action<XmlNode> elementVisitor)
{
if (doc != null && elementVisitor != null)
{
foreach (XmlNode node in doc.ChildNodes)
{
DoIterateNode(node, elementVisitor);
}
}
}
public static void IterateThrough(this XmlNodeList nodes, Action<XmlNode> elementVisitor)
{
if (nodes != null && elementVisitor != null)
{
foreach (XmlNode node in nodes)
{
DoIterateNode(node, elementVisitor);
}
}
}
private static void DoIterateNode(XmlNode node, Action<XmlNode> elementVisitor)
{
elementVisitor(node);
foreach (XmlNode childNode in node.ChildNodes)
{
DoIterateNode(childNode, elementVisitor);
}
}
then i can update my xml nodes as below
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("~/xmlmail.xml"));
var email = new XmlEmail();
doc.IterateThroughAllNodes(
delegate(XmlNode node)
{
if (node.Name.Equals("requestdate"))
node.InnerText= email.RequestDate.ToLongDateString();
if (node.Name.Equals("vehicle"))
{
XmlNodeList childs = node.ChildNodes;
childs.IterateThrough(delegate(XmlNode vnode)
{
if (vnode.Name.Equals("id"))
vnode.InnerText= email.VehicleId.ToString();
if (vnode.Name.Equals("year"))
vnode.InnerText= email.Year.ToString();
if (vnode.Name.Equals("make"))
vnode.InnerText= email.Make;
if (vnode.Name.Equals("model"))
vnode.InnerText= email.Model;
if (vnode.Name.Equals("vin"))
vnode.InnerText= email.Vin;
if (vnode.Name.Equals("trim"))
vnode.InnerText = email.Trim;
});
}
if (node.Name.Equals("customer"))
{
XmlNodeList childs = node.ChildNodes;
childs.IterateThrough(delegate(XmlNode vnode)
{
if (vnode.Attributes != null && (vnode.Name.Equals("name") && vnode.Attributes["part"].Value.Equals("first")))
vnode.InnerText= email.FirstName;
if (vnode.Attributes != null && (vnode.Name.Equals("name") && vnode.Attributes["part"].Value.Equals("last")))
vnode.InnerText= email.LastName;
if (vnode.Name.Equals("email"))
vnode.InnerText= email.Email;
if (vnode.Name.Equals("phone"))
vnode.InnerText= email.Phone;
if (vnode.Name.Equals("comments"))
vnode.InnerText= email.Comments;
if (vnode.Name.Equals("address"))
{
XmlNodeList addresschilds = vnode.ChildNodes;
addresschilds.IterateThrough(delegate(XmlNode anode)
{
if (anode.Name.Equals("street"))
anode.InnerText= email.Street;
if (anode.Name.Equals("city"))
anode.InnerText= email.City;
if (anode.Name.Equals("phone"))
anode.InnerText= email.Phone;
if (anode.Name.Equals("regioncode"))
anode.InnerText= email.RegionCode;
if (anode.Name.Equals("postalcode"))
anode.InnerText= email.Postalode;
if (anode.Name.Equals("country"))
anode.InnerText= email.Country;
});
}
});
}
});
I have an xml file like this:
<servers>
<general name="1">
<service name="ser1"/>
<service name="ser2"/>
</general>
<general name="2">
<service name="ser1"/>
<service name="ser2"/>
</general>
</servers>
In my winform application, I have a treeview list with checkbox property set to true.What I am trying to achieve is that I am attempting to read this xml file and update both the parent and child node to this tree view.
What I have tried is:
XDocument doc = XDocument.Load(#"D:\\path.xml");
TreeNode node;
var gnrl = from general in doc.Descendants("general")
select new
{
parent = general.Attribute("name").Value,
child = general.Descendants("service")
};
//Loop through results
foreach (var general in gnrl)
{
// Add a root node.
node = dcselectview.Nodes.Add(String.Format(general.parent));
foreach (var ser in general.child)
{
// Add a node as a child of the previously added node.
node = node.Nodes.Add(String.Format(ser.Attribute("name").Value));
}
}
it reads the file and all details are updated but not in a proper way. rather it is shown as below:
Needed:
I want the parent element to be on top and down-right to it,the child elements. If possible, it would be nice if I dont have checkboxes for parent elements.
Any help would be really appreciated..
EDIT:
My code edited. Now I am getting as shown in new picture below:
I want the 2 black lines to be in same line,not as child node of another..
Do you want a hierarchical structure, like this?
If so, I recommend you to look at the Treeview:
http://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.checkboxes.aspx
Try this you will get
XDocument doc = XDocument.Load(#"D:\\test.xml");
IEnumerable<XElement> Xele = doc.XPathSelectElements("//general");
foreach (XElement xe in Xele.Descendants())
{
//MessageBox.Show(xe.Attribute("name").Value);
dcselectview.Parent.Text =xe.Parent.Attribute("name").Value; // here parent value ----> name="1" and name="2"
dcselectview.Nodes.Add(xe.Attribute("name").Value); // ser1 ser2 ser1 ser2
}
Try this:
public static class TreeViewExtension
{
public static bool LoadNodesFromXML(this TreeView tv, string xml)
{
try
{
XDocument doc = XDocument.Parse(xml);
TreeNode rootNode = new TreeNode();
rootNode.Text = doc.Root.ToString().Split('>')[0] + ">";
rootNode.LoadTreeNodes(doc.Root.Elements());
tv.Nodes.Add(rootNode);
return true;
}
catch { return false; }
}
public static void LoadTreeNodes(this TreeNode parentNode, IEnumerable<XElement> elements)
{
foreach (var e in elements) {
TreeNode childNode = new TreeNode();
childNode.Text = e.ToString().Split('>')[0] + ">";
parentNode.Nodes.Add(childNode);
childNode.LoadTreeNodes(e.Elements());
}
}
}
//Usage:
var yourInputXMLString = "<servers><general name=\"1\"><service name=\"ser1\"/>" +
"<service name=\"ser2\"/></general><general name=\"2\">" +
"<service name=\"ser1\"/><service name=\"ser2\"/>" +
"</general></servers>";
treeView1.LoadNodesFromXML(yourInputXMLString);
You have to add parent as a node first
public static bool LoadNodesFromXML()
{
XDocument doc = XDocument.Load(#"D:\\path.xml");
var root = doc.Root;
var childenode = dcselectview.Nodes.Add(root.Attribute("Name").Value);
foreach (var xElement in root .Elements())
{
InsertNode(childenode, xElement);
}
}
private void InsertNode(TreeNode parent, XElement element)
{
var childenode = parent.Nodes.Add(element.Attribute("Name").Value);
if(element.Elements().Count() > 0)
foreach (var xElement in element.Elements())
{
InsertNode(childenode, xElement);
}
}
Thanks all for your help::But I have found another solution of my own::
XDocument doc = XDocument.Load(#"path\\test.xml");
// Add nodes to treeView1.
TreeNode pnode;
TreeNode cnode;
var gnrl = from general in doc.Descendants("general")
select new
{
parent = general.Attribute("name").Value,
child = general.Descendants("service")
};
//Loop through results
foreach (var general in gnrl)
{
// Add a root node.
pnode = treeview.Nodes.Add(String.Format(general.parent));
foreach (var ser in general.child)
{
// Add a node as a child of the previously added node.
cnode = pnode.Nodes.Add(String.Format(ser.Attribute("name").Value));
}
}
<?xml version="1.0" encoding="utf-8" ?>
<testcase>
<date>4/12/13</date>
<name>Mrinal</name>
<subject>xmlTest</subject>
</testcase>
I am trying to read the above xml using c#, But i get null exception in the try catch block can any body suggest the required change.
static void Main(string[] args)
{
XmlDocument xd = new XmlDocument();
xd.Load("C:/Users/mkumar/Documents/testcase.xml");
XmlNodeList nodelist = xd.SelectNodes("/testcase"); // get all <testcase> nodes
foreach (XmlNode node in nodelist) // for each <testcase> node
{
CommonLib.TestCase tc = new CommonLib.TestCase();
try
{
tc.name = node.Attributes.GetNamedItem("date").Value;
tc.date = node.Attributes.GetNamedItem("name").Value;
tc.sub = node.Attributes.GetNamedItem("subject").Value;
}
catch (Exception e)
{
MessageBox.Show("Error in reading XML", "xmlError", MessageBoxButtons.OK);
}
........
.....
The testcase element has no attributes. You should be looking to it's child nodes:
tc.name = node.SelectSingleNode("name").InnerText;
tc.date = node.SelectSingleNode("date").InnerText;
tc.sub = node.SelectSingleNode("subject").InnerText;
You might process all nodes like this:
var testCases = nodelist
.Cast<XmlNode>()
.Select(x => new CommonLib.TestCase()
{
name = x.SelectSingleNode("name").InnerText,
date = x.SelectSingleNode("date").InnerText,
sub = x.SelectSingleNode("subject").InnerText
})
.ToList();
You can use LINQ to XML to select all testcase elements from your xml and parse them to TestCase instances:
var xdoc = XDocument.Load("C:/Users/mkumar/Documents/testcase.xml");
var testCases = from tc in xdoc.Descendants("testcase")
select new CommonLib.TestCase {
date = (string)tc.Element("date"),
name = (string)tc.Element("name"),
sub= (string)tc.Element("subject")
};
BTW you have only one testcase element currently, which is root of XML file. So, you can do instead:
var tc = XElement.Load("C:/Users/mkumar/Documents/testcase.xml");
var testCase = new CommonLib.TestCase {
date = (string)tc.Element("date"),
name = (string)tc.Element("name"),
sub= (string)tc.Element("subject")
};
private static void Main(string[] args)
{
XmlDocument xd = new XmlDocument();
xd.Load("C:\\test1.xml");
XmlNodeList nodelist = xd.SelectNodes("/testcase"); // get all <testcase> nodes
foreach (XmlNode node in nodelist) // for each <testcase> node
{
try
{
var name = node.SelectSingleNode("date").InnerText;
var date = node.Attributes.GetNamedItem("name").Value;
var sub = node.Attributes.GetNamedItem("subject").Value;
}
catch (Exception e)
{
MessageBox.Show("Error in reading XML", "xmlError", MessageBoxButtons.OK);
}
}
This will work I have test it #Alex correct answer
You are trying to read attributes whereas date, name and subject are not attributes. They are subnodes.
your code should be like this
XmlDocument xd = new XmlDocument();
xd.Load("test.xml");
XmlNodeList nodelist = xd.SelectNodes("/testcase"); // get all <testcase> nodes
foreach (XmlNode node in nodelist) // for each <testcase> node
{
try
{
string name = node.SelectSingleNode("name").InnerText;
string date = node.SelectSingleNode("date").InnerText;
string sub = node.SelectSingleNode("subject").InnerText;
}
catch (Exception ex)
{
MessageBox.Show("Error in reading XML", "xmlError", MessageBoxButtons.OK);
}
}
Your Xml do not contain Attributes. date, name and subject - it's Child Nodes of the testcase Node.
Try this:
...
tc.name = node["name"].InnerText;
...
or this:
...
tc.name = node.SelectSingleNode("name").InnerText;
...
I'm trying to figure out the simplest way to take xml like this:
<Car>
<Description Model="Ford ">Blue </Description>
</Car>
into this:
<Car>
<Description Model="Ford">Blue</Description>
</Car>
Using LINQ to XML, how about something like:
foreach (var element in doc.Descendants())
{
foreach (var attribute in element.Attributes())
{
attribute.Value = attribute.Value.Trim();
}
foreach (var textNode in element.Nodes().OfType<XText>())
{
textNode.Value = textNode.Value.Trim();
}
}
I think that should work... I don't believe you need to use ToList to avoid disturbing things as you iterate, as you're not changing the structure of the XML document, just the text.
Try this. Don't forget to recurse through your ChildNodes ...
protected void Page_Load(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load(#"c:\temp\cars.xml");
Recurse(doc.ChildNodes);
}
private void Recurse(XmlNodeList nodes)
{
foreach (XmlNode node in nodes)
{
if (node.InnerText != null)
node.InnerText = node.InnerText.Trim();
if (node.Attributes != null)
{
foreach (XmlAttribute att in node.Attributes)
att.Value = att.Value.Trim();
}
Recurse(node.ChildNodes);
}
}
If you're not using or can't use LINQ to XML then the below worked well for me with XmlDocument
TrimXmlText(xmlDocument.ChildNodes);
private void TrimXmlText(XmlNodeList xmlNodeList)
{
foreach (XmlNode xmlNode in xmlNodeList)
{
if (xmlNode.NodeType == XmlNodeType.Text)
{
xmlNode.InnerText = xmlNode.InnerText?.Trim();
}
else
{
TrimXmlText(xmlNode.ChildNodes);
}
}
}
Currently I have the following code:
XmlDocument xDoc = new XmlDocument();
xDoc.Load("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=twitter");
XmlNodeList tweets = xDoc.GetElementsByTagName("text");
foreach (int i in tweets)
{
if (tweets[i].InnerText.Length > 0)
{
MessageBox.Show(tweets[i].InnerText);
}
}
Which doesn't work, it gives me System.InvalidCastException on the foreach line.
The following code works perfectly (no foreach, the i is replaced with a zero):
XmlDocument xDoc = new XmlDocument();
xDoc.Load("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=twitter");
XmlNodeList tweets = xDoc.GetElementsByTagName("text");
if (tweets[0].InnerText.Length > 0)
{
MessageBox.Show(tweets[0].InnerText);
}
I know that there is already a marked answer, but you can do it like you did in your first try, you just need to replace the int with XmlNode
XmlDocument xDoc = new XmlDocument();
xDoc.Load("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=twitter");
XmlNodeList tweets = xDoc.GetElementsByTagName("text");
foreach (XmlNode i in tweets)
{
if (i.InnerText.Length > 0)
{
MessageBox.Show(i.InnerText);
}
}
tweets is a node list. I think that what you're trying to do is this:
XmlDocument xDoc = new XmlDocument();
xDoc.Load("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=twitter");
XmlNodeList tweets = xDoc.GetElementsByTagName("text");
for (int i = 0; i < tweets.Count; i++)
{
if (tweets[i].InnerText.Length > 0)
{
MessageBox.Show(tweets[i].InnerText);
}
}
It is not of Int type, That is the reason you are getting a casting exception. You can either replace int with the appropriate type or simply make use of type inference (implicitly typed variables) to handle this. Here i am using typeinference.by saying type as var, The compiler will understand it is of type of the iterator variable in tweets collection
foreach (var i in tweets)
{
if (i!=null)
{
string tweet= (((System.Xml.XmlElement)(i))).InnerText;
MessageBox.Show(tweet);
}
}
EDIT : With the Wonderful LINQtoXML, Your code can be rewritten like this.
string url = "http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=twitter";
XElement elm = XElement.Load(url);
if (elm != null)
{
foreach (var status in elm.Elements("status"))
{
string tweet = status.Element("text").Value;
MessageBox.Show(ss);
}
}
All the answers seem to be a bit outdated Imperative examples so I will add a declarative one. This is not doing what the OP wanted but I'm sure you'll get the point.
public static List<System.Xml.XmlNode> toList(System.Xml.XmlNodeList nodelist){
List<System.Xml.XmlNode> nodes = new List<System.Xml.XmlNode>();
foreach (System.Xml.XmlNode node in nodelist)
{
nodes.Add(node);
}
return nodes;
}
public static ReadMeObject setXml(ReadMeObject readmeObject){
readmeObject.xmlDocument = new System.Xml.XmlDocument();
readmeObject.xmlDocument.LoadXml("<body>"+readmeObject.htmlStringContent+"</body>");
System.Xml.XmlNodeList images = readmeObject.xmlDocument.SelectNodes("//img");
Array.ForEach(
Functions.toList( images )
.Where((image) => image.Attributes != null)
.Where((image) => image.Attributes["src"] != null)
.Where((image) => image.Attributes["src"].Value != "")
.ToArray()
, (image) => {
Console.WriteLine(image.Attributes["src"].Value);
}
);
return readmeObject;
}
foreach (XmlNode node in tweets)
{
if (tweets[i].InnerText.Length > 0)
{
MessageBox.Show(tweets[node].InnerText);
}
}
I've changed the 'I', which you cannot use, to XmlNode, which selects a single line of your list.
You can loop through the Collection with .GetEnumerator()
this code is taken Microsoft Documentation :
XmlNodeList elemList = root.GetElementsByTagName("title");
IEnumerator ienum = elemList.GetEnumerator();
while (ienum.MoveNext()) {
XmlNode title = (XmlNode) ienum.Current;
Console.WriteLine(title.InnerText);
}
Use this simple extension method to iterate through XmlNodeList:
public static void ForEachXml<TXmlNode>(this XmlNodeList nodeList, Action<TXmlNode> action)
{
foreach (TXmlNode node in nodeList) action(node);
}
Method Call:
xDoc.GetElementsByTagName("text").ForEachXML<XmlNode>(tweet =>
{
if (tweet.InnerText.Length > 0)
MessageBox.Show(tweet.InnerText);
});