C# modify specific node XML and delete all node with same attribute - c#

Well I got XML like that:
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<!--Alert notes-->
<Aletrs>
<Alert>
<Name>Counter</Name>
<Value>0</Value>
</Alert>
<Alert>
<ID>1</ID>
<Name>02:46:10 - Alert ID.1 nr.1 Camera1 - photo</Name>
<Value>Capture/Images/Camera1_snapshoot_nr1-2014-03-02_02-46-10.png</Value>
</Alert>
<Alert>
<ID>1</ID>
<Name>02:46:11 - Alert ID.1 nr.2 Camera1 - photo</Name>
<Value>Capture/Images/Camera1_snapshoot_nr2-2014-03-02_02-46-11.png</Value>
</Alert>
</Alerts>
At first I need help in modify number in Value in Counter name Alert node, I got the code how to get this value but I failed to modify it into changing value and save modified XML:
string index = (from xml2 in dailyXML.Descendants("Alert")
where xml2.Element("Name").Value == "Counter"
select xml2.Element("Value").Value).FirstOrDefault();
Also I need help in code deleting all "Alert" nodes which have same value in < ID>..< /ID>

var xDoc = XDocument.Load(filename);
/*1*/
var value = xDoc.Descendants("Alert")
.FirstOrDefault(a => a.Element("Name").Value == "Counter")
.Element("Value");
value.Value = "1";
/*2*/
foreach(var alert in xDoc.Descendants("Alert")
.GroupBy(a => (string)a.Element("ID")))
{
alert.Skip(1).ToList().ForEach(x => x.Remove());
}

Related

I am having an xml inside xml and want to test a condition is met inside the inner xml. C# solution required

<?xml version="1.0"?>
<TextType IsKey="false" Name="XMLReport"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Providers
xmlns="Reporting"/>
<Sales
xmlns="Reporting"/>
<Value
xmlns="Reporting">
<?xml version="1.0" encoding="utf-8"?>
<TestReport>
<StudyUid>
<![CDATA[123]]>
</StudyUid>
<Modality>
<![CDATA[XYZ]]>
</Modality>
<StudyDate format="DICOM">123456</StudyDate>
<StudyTime format="DICOM">6789</StudyTime>
<AccessionNumber>
<![CDATA[123]]>
</AccessionNumber>
<StudyDescription>
<![CDATA[abc def]]>
</StudyDescription>
<OperatorName format="xyz">
<![CDATA[abc]]>
</OperatorName>
<PhysicianReadingStudy format="xyz">
<![CDATA[^^^^]]>
</PhysicianReadingStudy>
<InstitutionName>
<![CDATA[xyz]]>
</InstitutionName>
<HospitalName>
<![CDATA[Hospital Name]]>
</HospitalName>
<ReportSet>
<MyReport ID="1">
<ReportStatus>
<![CDATA[Done]]>
</ReportStatus>
</MyReport>
<MyReport ID="2">
<ReportStatus>
<![CDATA[Done]]>
</ReportStatus>
</MyReport>
<MyReport ID="3">
<ReportStatus>
<![CDATA[Initial]]>
</ReportStatus>
</MyReport>
</ReportSet>
<ReportImageSet />
<FetusSet />
</TestReport>
</Value>
<WhoSetMe xmlns="Reporting">NotSpecified
</WhoSetMe>
</TextType>
I want to parse the xml above in C# and check whether "ReportStatus" is "Done" for all the ReportStatus under MyReport/ReportSet. One more twist here is the xml contains one more xml starts at "Value" tag as in above example.It may contatin many ReportStatus tag under ReportSet tag. Can someone please help me?
// Can you try this? I tried to do it with LINQ to XML.
// I assume you have multiple <TestReport /> elements in <Value /> tag
// and var xelement is your xml variable
// First we get all TestReport elemnts
IEnumerable<XElement> allReports =
from el in xelement.Elements("TextType/Value/TestReport")
select el;
// From allReports we get all MyReport elemnts
IEnumerable<XElement> allMyReports =
from el in allReports.Elements("ReportSet/MyReport")
select el;
// From allReports we also get all MyReport elemnts with element ReportStatus value equals "Done"
IEnumerable<XElement> allDoneMyReports =
from el in allMyReports
where (string)el.Element("ReportStatus") == "Done"
select el;
// Now we compare allMyReport with allDoneMyReports
if (allMyReports.Count() == allDoneMyReports.Count())
{
//DO Somehing
}
Your XML document is invalid. You need to fix it before trying to parse it. The issue is that a document can only have one top-level element; you have 2 <TextType> and <Providers>.
Most of your elements are the namespace Reporting. You need to use it when referencing the element.
XNamespace ns = "Reporting";
var value = doc.Element("Value" + ns);
Update
Just use the namespace for each element
XNamespace ns = "Reporting";
var value = xelement.Elements("Value" + ns);
Another Update
The XML document is considered invalid because it has multiple XML declarations; there is no way to disable this. I suggest you pre-process the document to remove the extra declarations. Here's an example (https://dotnetfiddle.net/UnuAF6)
var xml = "<?xml version='1.0'?><a> <?xml version='1.0'?><b id='b' /></a>";
var doc = XDocument.Parse(xml.Replace(" <?xml version='1.0'?", " "));
var bs = doc.Descendants("b");
Console.WriteLine("{0} 'b' elements", bs.Count());

Using Linq to filter out specific element content

I'm trying to use LINQ in a .Net application to filter out personal data from the XML I save to a database.
Sample XML
<?xml version="1.0" encoding="UTF-8"?>
<body>
<Details>
<Id>1</Id>
<objectList>
<object>
<Key>Account</Key>
<Value>12345</Value>
</object>
<object>
<Key>Password</Key>
<Value>abcd</Value>
</object>
</objectList>
</Details>
</Body>
What I have working:
var xmlFile = File.ReadAllText("Test.xml");
var xDoc = XDocument.Parse(xmlFile);
var newXDoc = xDoc.Descendants().Where(e => e.Name.LocalName == "Value").ToList().ForEach(e => e.Value = "FILTERED");
However, this filters the value you for all objects.
What I'm looking to do is a filter the value for specific objects only, say, where Key equals "Password"
Is there a way to do this?
You can do the following:
var objectValue="Password";
var result= xDoc.Descendants("object").Where(e => e.Element("Key").Value==objectValue);
foreach(var e in result)
{
e.Element("Value").Value = "FILTERED";
}
First filter the objects according to the condition you want to apply and then change the value of the Value node as I show above.

Search through XML and grab another Node

<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>1</MessageID>
<Product>
<SKU>33333-01</SKU>
</Product>
</Message>
</Envelope>
I've tried googling but whether I'm just not providing the correct search criteria I don't know.
I want to be able to search the XML file based on the MessageID and then grab the SKU.
I then want to search another XML file based on the SKU and remove that message completely.
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>1</MessageID>
<Inventory>
<SKU>33333-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
<Message>
<MessageID>2</MessageID>
<Inventory>
<SKU>22222-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
</Envelope>
Meaning the XML above becomes:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<MessageID>2</MessageID>
<Inventory>
<SKU>22222-01</SKU>
<Quantity>1</Quantity>
</Inventory>
</Message>
</Envelope>
To confirm I cannot confirm that the MessageID will be the same over different XML files.
Thanks in advance for any help.
My questions:
How do I search through XML files?
How do I then grab another Nodes details
Can I remove a complete from an XML file based on a search?
You can use XmlDocument to load your XML document. Then, you can use XPath for searching any nodes.
XmlDocument document = new XmlDocument();
document.Load("C:\fileOnTheDisk.xml");
// or
document.LoadXml("<a>someXmlString</a>");
// Returns single element or null if not found
var singleNode = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
// Returns a NodeList
var nodesList = document.SelectNodes("Envelope/Message[MessageID = '1']");
Read more about XPath at w3schools.com.
Here is a good XPath Tester.
For example, you can use the following XPath to find nodes in your document by ID:
XmlDocument document = new XmlDocument();
document.Load("C:\doc.xml");
var node = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
var sku = node.SelectSingleNode("Inventory/SKU").InnerText;
Console.WriteLine("{0} node has SKU = {1}", 1, sku);
Or you can output all SKUs:
foreach (XmlNode node in document.SelectNodes("Envelope/Message"))
{
Console.WriteLine("{0} node has SKU = {1}",
node.SelectSingleNode("MessageID").InnerText,
node.SelectSingleNode("Inventory/SKU").InnerText);
}
It will produce:
1 node has SKU = 33333-01
2 node has SKU = 22222-01
Note that there are possible NullReferenceExceptions if nodes are not present.
You can simply remove it using RemoveChild() method of its parent.
XmlDocument document = new XmlDocument();
document.Load("C:\doc.xml");
var node = document.SelectSingleNode("Envelope/Message[MessageID = '1']");
node.ParentNode.RemoveChild(node);
document.Save("C:\docNew.xml"); // will be without Message 1
You can use Linq to XML to do this:
var doc= XDocument.Load("input.xml");//path of your xml file in which you want to search based on message id.
var searchNode= doc.Descendants("MessageID").FirstOrDefault(d => d.Value == "1");// It will search message node where its value is 1 and get first of it
if(searchNode!=null)
{
var SKU=searchNode.Parent.Descendants("SKU").FirstOrDefault();
if(SKU!=null)
{
var searchDoc=XDocument.Load("search.xml");//path of xml file where you want to search based on SKU value.
var nodes =searchDoc.Descendants("SKU").Where(d=>d.Value==SKU.Value).Select(d=>d.Parent.Parent).ToList();
nodes.ForEach(node=>node.Remove());
searchDoc.Save("output.xml");//path of output file
}
}
I'd recommend you did this using LINQ to XML - it's much nicer to work with than the old XmlDocument API.
For all the examples, you can parse your XML string xml to an XDocument like so:
var doc = XDocument.Parse(xml);
1. How do I search through XML files?
You can get the SKU for a specific message ID by querying your document:
var sku = (string)doc.Descendants("Message")
.Where(e => (int)e.Element("MessageID") == 1)
.SelectMany(e => e.Descendants("SKU"))
.Single();
2. How do I then grab another Nodes details?
You can get the Message element with a specified SKU using a another query:
var message = doc.Descendants("SKU")
.Where(sku => (string)sku == "33333-01")
.SelectMany(e => e.Ancestors("Message"))
.Single();
3. Can I remove a complete element from an XML file based on a search?
Using your result from step 2, you can simple call Remove:
message.Remove();
Alternatively, you can combine the query from step 2 and simply execute a command to remove any messages that have a specific SKU:
doc.Descendants("SKU")
.Where(sku => (string)sku == "33333-01")
.SelectMany(e => e.Ancestors("Message"))
.Remove();
I tried to answer all your questions:
using System.Xml.XPath;
using System.Xml.Linq;
XDocument xdoc1 = XDocument.Load("xml1.xml");
XDocument xdoc2 = XDocument.Load("xml2.xml");
string sku = String.Empty;
string searchedID = "2";
//1.searching through an xml file based on path
foreach (XElement message in xdoc1.XPathSelectElements("Envelope/Message"))
{
if (message.Element("MessageID").Value.Equals(searchedID))
{
//2.grabbing another node's details
sku = message.XPathSelectElement("Inventory/SKU").Value;
}
}
foreach (XElement message in xdoc2.XPathSelectElements("Envelope/Message"))
{
if (message.XPathSelectElement("Inventory/SKU") != null && message.XPathSelectElement("Inventory/SKU").Value.Equals(sku))
{
//removing a node
message.Remove();
}
}
xdoc2.Save("xml2_del.xml");
}

LINQ - Specified elements not being removed from XML file

I have an XML file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--QTabs Data Storage-->
<SyncTimes>
<LastSyncTime>
<id>1</id>
<SyncTime>3/31/2015 2:03:28 PM</SyncTime>
</LastSyncTime>
<LastSyncTime>
<id>2</id>
<SyncTime>3/31/2015 2:14:24 PM</SyncTime>
</LastSyncTime>
<LastSyncTime>
<id>3</id>
<SyncTime>3/31/2015 2:14:25 PM</SyncTime>
</LastSyncTime>
<LastSyncTime>
<id>4</id>
<SyncTime>3/31/2015 2:14:26 PM</SyncTime>
</LastSyncTime>
</SyncTimes>
All of the above times are earlier today I want to delete all LastSyncTime records before the current time (DateTime.Now):
public async void deleteArchivedSyncs()
{
var xElement = (from element in XMLDocObject.Elements("LastSyncTime")
where Convert.ToDateTime(element.Element("SyncTime").Value) < DateTime.Now
select element);
xElement.Remove();
storageFile = await storageFolder.GetFileAsync(Settings.xmlFile);
using (Stream fileStream = await storageFile.OpenStreamForWriteAsync())
{
XMLDocObject.Save(fileStream);
}
}
This being run does not effect the XML page. The desired elements are not being removed. What am I doing wrong?
This issue here appears to be that the only way to delete a child, is to have the parent do the deletion, as in:
class Program
{
public static void Main(params string[] args)
{
// test.xml contains OPs example content.
var xdoc = XDocument.Load(#"c:\temp\test.xml");
xdoc.Descendants("LastSyncTime")
.Where(e => Convert.ToDateTime(e.Element("SyncTime").Value) < DateTime.Now)
.Remove();
Console.WriteLine(xdoc);
xdoc.Save(#"c:\temp\test_filtered.xml");
}
}
This generates the following output:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--QTabs Data Storage-->
<SyncTimes />
I.e. an empty root, which is to be expected, given that all dates are smaller than DateTime.Now.
#DavidTunnell what is your root xml element that contains everything you need? in example:
//let's call the variable you use as Xdocument doc.
XmlNodeList nodes = doc.SelectNodes("LastSyncTime");
for (int i = nodes.Count - 1; i >= 0; i--)
{
nodes[i].ParentNode.RemoveChild(nodes[i]);
}
doc.Save(path);
This is how i had used hope it helps.

Getting Attribute's value from xml in c#

I have a LINQ expression which gets the XML attribute values from a xml file.
var xml = XElement.Load(#"C:\\StoreServer1.xml");
var query = from e in xml.Descendants("Groups")
where int.Parse(e.Element("Store").Value) == 1500
select e.Element("Store").Attribute("WeekDayStClose").Value;
And the xml file is:
enter<?xml version="1.0" encoding="utf-8" ?>
<Stores>
<Groups Range="000">
<Store WeekDayStClose="210" SatStClose="21" SunStClose="22">1500</Store>
<Store WeekDayStClose="23" SatStClose="24" SunStClose="25">18</Store>
<Store WeekDayStClose="23" SatStClose="24" SunStClose="25">19</Store>
</Groups>
</Stores>
I am only getting the attribute result (value) for first element of 1500. If I search same thing for 18 it doesn't return any result and no exception. Any help appreciated....Plz help!!!
Try this out:-
var xml = XElement.Load(#"C:\\StoreServer1.xml");
var query = xml.Descendants("Groups").Descendants("Store").Where(e => int.Parse(e.Value) == 18).Select(e=> e.Attribute("WeekDayStClose").Value);
You should be more granular, call sub Descendants with Store (XName):
var xml = XElement.Load(#"C:\\New Folder\\StoreServer1.xml");
var query = from e in xml.Descendants("Groups").Descendants("Store")
where int.Parse(e.Value) == 18
select e.Attribute("WeekDayStClose").Value;
Because now you're retrieving only the first Store of each Group which is 1500.
Yes, you have a little error in your code:
You are splitting your xml into group-elements (you have just one group). Then you check if the first store element has the value 1500 (you are not checking if the following store elements have maybe the value 1500)
You need to change your code into the following
var query = from e in xml.Descendants("Store")
where int.Parse(e.Value) == 1500
select e.Attribute("WeekDayStClose").Value;

Categories