A better way to handle XML updation - c#

I have a DataGridView control where some values are popluted.
And also I have an xml file. The user can change the value in the Warning Column of DataGridView.And that needs to be saved in the xml file.
The below program just does the job
XDocument xdoc = XDocument.Load(filePath);
//match the record
foreach (var rule in xdoc.Descendants("Rule"))
{
foreach (var row in dgRulesMaster.Rows.Cast<DataGridViewRow>())
{
if (rule.Attribute("id").Value == row.Cells[0].Value.ToString())
{
rule.Attribute("action").Value = row.Cells[3].Value.ToString();
}
}
}
//save the record
xdoc.Save(filePath);
Matching the grid values with the XML document and for the matched values, updating the needed XML attribute.
Is there a better way to code this?
Thanks

You could do something like this:
var rules = dgRulesMaster.Rows.Cast<DataGridViewRow>()
.Select(x => new {
RuleId = x.Cells[0].Value.ToString(),
IsWarning = x.Cells[3].Value.ToString() });
var tuples = from n in xdoc.Descendants("Rule")
from r in rules
where n.Attribute("id").Value == r.RuleId
select new { Node = n, Rule = r };
foreach(var tuple in tuples)
tuple.Node.Attribute("action").Value = tuple.Rule.IsWarning;
This is basically the same, just a bit more LINQ-y. Whether or not this is "better" is debatable. One thing I removed is the conversion of IsWarning first to string, then to int and finally back to string. It now is converted to string once and left that way.

XPath allows you to target nodes in the xml with alot of power. Microsoft's example of using the XPathNavigator to modify an XML file is as follows:
XmlDocument document = new XmlDocument();
document.Load("contosoBooks.xml");
XPathNavigator navigator = document.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("bk", "http://www.contoso.com/books");
foreach (XPathNavigator nav in navigator.Select("//bk:price", manager))
{
if (nav.Value == "11.99")
{
nav.SetValue("12.99");
}
}
Console.WriteLine(navigator.OuterXml);
Source: http://msdn.microsoft.com/en-us/library/zx28tfx1(v=vs.80).aspx

Related

how to get value from xml by Linq

i was reading huge xml file of 5GB size by using the following code, and i was success to get the first element Testid but failed to get another element TestMin coming under different namespace
this is the xml i am having
which i am getting as null
.What is wrong here?
EDIT
GMileys answer giving error like The ':' character, hexadecimal value 0x3A, cannot be included in a name
The element es:qRxLevMin is a child element of xn:attributes, but it looks like you are trying to select it as a child of xn:vsDataContainer, it is a grandchild of that element. You could try changing the following:
var dataqrxlevmin = from atts in pin.ElementsAfterSelf(xn + "VsDataContainer")
select new
{
qrxlevmin = (string)atts.Element(es + "qRxLevMin"),
};
To this:
var dataqrxlevmin = from atts in pin.Elements(string.Format("{0}VsDataContainer/{1}attributes", xn, es))
select new
{
qrxlevmin = (string)atts.Element(es + "qRxLevMin"),
};
Note: I changed your string concatenation to use string.Format for readability purposes, either is technically fine to use, but string.Format is a better approach.
What about this approach?
XDocument doc = XDocument.Load(path);
XName utranCellName = XName.Get("UtranCell", "un");
XName qRxLevMinName = XName.Get("qRxLevMin", "es");
var cells = doc.Descendants(utranCellName);
foreach (var cell in cells)
{
string qRxLevMin = cell.Descendants(qRxLevMinName).FirstOrDefault();
// Do something with the value
}
try this code which is very similar to your code but simpler.
using (XmlReader xr = XmlReader.Create(path))
{
xr.MoveToContent();
XNamespace un = xr.LookupNamespace("un");
XNamespace xn = xr.LookupNamespace("xn");
XNamespace es = xr.LookupNamespace("es");
while (!xr.EOF)
{
if(xr.LocalName != "UtranCell")
{
xr.ReadToFollowing("UtranCell", un.NamespaceName);
}
if(!xr.EOF)
{
XElement utranCell = (XElement)XElement.ReadFrom(xr);
}
}
}
actually namespace was the culprit,what i did is first loaded the small section i am getting from.Readform method in to xdocument,then i removed all the namespace,then i took the value .simple :)

Get certain xml node and save the value

Considering the following XML:
<Stations>
<Station>
<Code>HT</Code>
<Type>123</Type>
<Names>
<Short>H'bosch</Short>
<Middle>Den Bosch</Middle>
<Long>'s-Hertogenbosch</Long>
</Names>
<Country>NL</Country>
</Station>
</Stations>
There are multiple nodes. I need the value of each node.
I've got the XML from a webpage (http://webservices.ns.nl/ns-api-stations-v2)
Login (--) Pass (--)
Currently i take the XML as a string and parse it to a XDocument.
var xml = XDocument.Parse(xmlString);
foreach (var e in xml.Elements("Long"))
{
var stationName = e.ToString();
}
You can retrieve "Station" nodes using XPath, then get each subsequent child node using more XPath. This example isn't using Linq, which it looks like you possibly are trying to do from your question, but here it is:
XmlDocument xml = new XmlDocument();
xml.Load(xmlStream);
XmlNodeList stations = xml.SelectNodes("//Station");
foreach (XmlNode station in stations)
{
var code = station.SelectSingleNode("Code").InnerXml;
var type = station.SelectSingleNode("Type").InnerXml;
var longName = station.SelectSingleNode("Names/Long").InnerXml;
var blah = "you should get the point by now";
}
NOTE: If your xmlStream variable is a String, rather than a Stream, use xml.LoadXml(xmlStream); for line 2, instead of xml.Load(xmlStream). If this is the case, I would also encourage you to name your variable to be more accurately descriptive of the object you're working with (aka. xmlString).
This will give you all the values of "Long" for every Station element.
var xml = XDocument.Parse(xmlStream);
var longStationNames = xml.Elements("Long").Select(e => e.Value);

How to get XML Elements from XmlDocument object?

Assume that an XmlDocument is successfully loaded with this code:
var doc = new XmlDocument();
doc.Load(stream);
This is a sample portion of the XML stream (the complete XML stream has about 10000 of ProductTable's):
<ProductTable>
<ProductName>Chair</ProductName>
<Price>29.5</Price>
</ProductTable>
Using Linq, how do I access the ProductName and Price elements? Thanks.
I suggest using an XDocument instead of an XmlDocument (the latter is not suited for LINQ to XML). Use the XDocument.Load(...) method to load your "real" XML.
string xml = #"<ProductTable>
<ProductName>Chair</ProductName>
<Price>29.5</Price>
</ProductTable>";
XDocument x = XDocument.Parse(xml);
var tables = x.Descendants("ProductTable");
Dictionary<string,string> products = new Dictionary<string, string>();
foreach (var productTable in tables)
{
string name = productTable.Element("ProductName").Value;
string price = productTable.Element("Price").Value;
products.Add(name, price);
}
If you'd prefer to use sugar-coated SQL like syntax or would like to read up on the topic, this MSDN article is a great place to start.
The following is a more concise version if you feel like using an anonymous type:
XDocument document = XDocument.Parse(xml)
var products = /* products is an IEnumerable<AnonymousType> */
from item in document.Descendants("ProductTable")
select new
{
Name = item.Element("ProductName").Value,
Price = item.Element("Price").Value
};
You could then use this expressive syntax to print the matches in the console:
foreach (var product in products) /* var because product is an anonymous type */
{
Console.WriteLine("{0}: {1}", product.Name, product.Price);
}
Console.ReadLine();

C# - Unable to save a customized linq-to-xml file, with new elements in it?

I'm creating a new XDocument and inserting a root element "profiles" in it, then saving.
if (!System.IO.File.Exists("profiles.xml"))
{
XDocument doc = new XDocument(
new XElement("profiles")
);
doc.Save("profiles.xml", SaveOptions.None);
}
And then later I wanna take users input and add profiles into the already created xml file:
XElement profile =
new XElement(Player.Name,
new XElement("level", Player.Level),
new XElement("cash", Player.Cash)
);
XDocument doc = XDocument.Load("profiles.xml");
List<XElement> profiles = doc.Root.Elements().ToList();
for (int i = 0; i < profiles.Count; i++)
{
if (profiles[i].Name.ToString() == Player.name)
{
profiles[i] = profile;
return;
}
}
profile.Add(profile);
doc.Save("profiles.xml", SaveOptions.None);
But for some reason, it will never add any new profiles?
EDIT: Also, if I manually create a new profile into the xml file, it won't customize either, so the problem is within Saving the file?
You're never actually doing anything to change any of the elements within the XDocument that doc refers to:
If you find an element with the existing name, you're modifying the list, but that won't modify the document. You probably want to use XElement.ReplaceWith:
profiles[i].ReplaceWith(profile);
Note that in this case you're not even trying to save the XML file again (due to the return statement), so it's not really clear what you're trying to achieve in this case.
If you don't find the element, you're adding the profile element to itself, which certainly isn't going to modify the document. I suspect you want:
doc.Root.Add(profile);
In other words, add the new profile element as a new final child of the root element.
EDIT: Here's a different approach to try instead - I'm assuming any one name should only occur once:
XDocument doc = XDocument.Load("profiles.xml");
var existingElement = doc.Root
.Elements()
.Where(x => x.Name.ToString() == Player.name)
.FirstOrDefault();
if (existingElement != null)
{
existingElement.ReplaceWith(profile);
}
else
{
doc.Root.Add(profile);
}
doc.Save("profiles.xml", SaveOptions.None);
Also, I would strongly advise you not to use the player name as the element name. Use it as an attribute value or text value instead, e.g.
XElement profile =
new XElement("player",
new XAttribute("name", Player.Name),
new Attribute("level", Player.Level),
new XAttribute("cash", Player.Cash)
);
That way you won't have problems if the player name has spaces etc. You'd then need to change your query to:
var existingElement = doc.Root
.Elements()
.Where(x => (string) x.Attribute("name)" == Player.name)
.FirstOrDefault();

extract xml data using C# and XML DOM

I have an xml which looks like this:
<dfs:dataFields>
<d:REQUIREMENT_SPECIFICATION ProjectName="Test 1" ProjectWorkCode="909"
FunctionDepartmentName="X department" BrandApplicableName="All" ProjectManagerName=""
ProjectSponserName="" BackgroundDescription="others and users use the Online tool to
to add users"
StepChangeGoalDescription="In 2011, the new service will be active" ServiceImpactedName="xy service"
xdado:OJsZDA="0">
</d:REQUIREMENT_SPECIFICATION>
</dfs:dataFields>
I need to extract the data which is in the quotes. For example, I want it to print out:
Requirement Specification
Project Name: Test 1
ProjectWorkCode: 909
FunctionDepartmentName: X department
...... and so on...
I am using the following code. it's printing out d:REQUIREMENT_SPECIFICATION and dfs:dataFields but won't print anything else.
XPathNavigator nav;
XPathDocument docNav;
docNav = new XPathDocument("test.xml");
nav = docNav.CreateNavigator();
nav.MoveToRoot();
//Move to the first child node (comment field).
nav.MoveToFirstChild();
do
{
//Find the first element.
if (nav.NodeType == XPathNodeType.Element)
{
//Determine whether children exist.
if (nav.HasChildren == true)
{
//Move to the first child.
nav.MoveToFirstChild();
Console.WriteLine(nav.Name);
Console.WriteLine(nav.Value);
//Loop through all of the children.
do
{
//Display the data.
nav.MoveToFirstChild();
Console.WriteLine(nav.Name);
Console.WriteLine(nav.Value);
} while (nav.MoveToNext());
}
}
} while (nav.MoveToNext());
//Pause.
Console.ReadLine();
Can you please point me in the right direction?
I prefer to use XmlDocument for such cases. You can define method which loads xml in the document and just return root node, and in the main method just loop through the Node's attributes:
private void ProcessAndDumpXml()
{
StreamReader xmlStream = new StreamReader("example1.xml");
XmlNode root = GetRootNode(xmlStream);
// process nodes
// ...
}
private XmlNode GetRootNode(StreamReader streamReader)
{
XmlDocument xmlDocument = new XmlDocument();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
nsmgr.AddNamespace("dfs", "schema1");
nsmgr.AddNamespace("d", "schema1");
nsmgr.AddNamespace("xdado", "schema1");
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlReaderSettings xset = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
XmlReader rd = XmlReader.Create(streamReader, xset, context);
xmlDocument.Load(rd);
return xmlDocument.DocumentElement.FirstChild;
}
In addition to looping through the children, you also need to loop through the attributes of each element.
The document you posted is not a valid XML document, because it lacks namespace specifications. But assuming the namespaces were present, you could do it using LINQ to XML like this:
var doc= XDocument.Load(xmlFile);
XNamespace dNs = "http://actual-d-namespace-uri";
foreach(var element in doc.Root.Elements(dNs + "REQUIREMENT_SPECIFICATION"))
{
var attributes = element.Attributes()
.Select(a => string.Format("{0}: {1}", a.Name, a.Value));
Console.WriteLine("Requirement Specification " + string.Join(" ", attributes));
}

Categories

Resources