I currently have an XML Structure that looks something like this
<Parent>
<Info>
<Info-Data></Info-Data>
<Info-Data2></Info-Data2>
</Info>
<Message>
<Foo></Foo>
<Bar></Bar>
</Message>
<Message>
<Foo/>
<Bar/>
</Message>
</Parent>
What I'm trying to accomplish is split each Message into it's own unique XDocument. I want it to be
<Parent>
<Info />
<Message />
</Parent>
I tried to do the following.
XDocument xDoc = XDocument.Parse(myXMLString);
IEnumerable<XElement> elements = xDoc.Descendants(xDoc.Root.Name.NameSpace + "Message");
foreach(XElement element in elements)
{
XDocument newDoc = XDocument.Parse(element.ToString());
}
Obviously this only gets me everything from Message and below. I tried using Ancestors and AncestorsAndSelf but they always include BOTH Messages. Is there a different call I should be making?
If your format is fixed like this, it's not so bad:
foreach(XElement element in elements)
{
XDocument newDoc = new XDocument
(new XElement(xDoc.Root.Name,
xDoc.Root.Element("Info"),
element));
// ...
}
It's not great, but it's not horrendous. An alternative is to clone the original document, remove all the Message elements, then repeatedly clone the "gutted" version and add one element at a time to the new clone:
XDocument gutted = new XDocument(xDoc);
gutted.Descendants(xDoc.Root.Name.Namespace + "Message").Remove();
foreach(XElement element in elements)
{
XDocument newDoc = new XDocument(gutted);
newDoc.Root.Add(element);
// ...
}
Related
I have the following xml file
<root xmlns="http://mynamespace">
<parent>
<first>text</first>
<second>more</second>
</parent>
<parent>
<first>2</first>
<second>3</second>
</parent>
<parent>
<first>aa</first>
<second>bb</second>
</parent>
</root>
I'm trying to get first and second children of parent.
C# seems to have problems with the following code (the error is on the last line):
var rawXml = #"<root xmlns=""http://mynamespace"">
<parent>
<first>text</first>
<second>more</second>
<third>hello</third>
</parent>
<parent>
<first>2</first>
<second>3</second>
<parent>
<first>a</first>
<second>b</second>
</parent>
</parent>
<parent>
<first>aa</first>
<second>bb</second>
</parent>
</root>";
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(rawXml);
var ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("m", "http://mynamespace");
var nav = xmlDoc.CreateNavigator();
var parents = nav.Select("//m:parent", ns);
Console.Write($"Got {parents.Count} parents.");
// this does not work
// error: Expression must evaluate to a node-set.
//var siblings = nav.Select("//m:parent/(m:first|m:second)", ns);
// but this does
var siblings = nav.Select("//m:parent/m:first|//m:parent/m:second", ns);
Console.Write($"Got {siblings.Count} children.");
Am I missing something? Is the first XPath expression wrong?
Is the first XPath expression wrong?
Yes, it's not valid XPath 1.0 syntax. You can't have a ( after a / in XPath 1.0.
You can achieve what you're trying to do, without repeating any node names, by using this path:
/m:root/m:parent/*[self::m:first or self::m:second]
Side note: avoid using // unless you have a specific reason to use it. It's bad for performance.
<?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());
I have an xml file like:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Session TimeStamp="2016-12-21T17:01:01.8642453+02:00">
<Message>
<Content>test1</Content>
<ID>1</ID>
<Timestamp>12/21/2016 17:01:01</Timestamp>
<EventType>Debug</EventType>
<Priority>High</Priority>
</Message>
<Message>
<Content>test2</Content>
<ID>2</ID>
<Timestamp>12/21/2016 17:01:01</Timestamp>
<EventType>Exception</EventType>
<Priority>Low</Priority>
</Message>
<Message>
<Content>test3</Content>
<ID>3</ID>
<Timestamp>12/21/2016 17:01:01</Timestamp>
<EventType>Info</EventType>
<Priority>Medium</Priority>
</Message>
<Message>
<Content>test4</Content>
<ID>4</ID>
<Timestamp>12/21/2016 17:01:01</Timestamp>
<EventType>Warn</EventType>
<Priority>None</Priority>
</Message>
</Session>
</Root>
I want to check the value of element Content in every message i have try with this method:
Assert.IsTrue(xDocument.Root.Elements("Session").Last().Elements("Message").First().Element("Content").Value.Contains("test1"));
exception: System.InvalidOperationException: Sequence contains no elements
The method fail, cant find the element value, how can i do it using xdocument?
Are you looking for this since you say
I want to check the value of element Content in every message
xDocument.Root.Elements("Session")
.Elements("Message")
.Elements("Content")
.Select(x => x.Value.Contains("test1"));
It would return which node contains test1 so the result would be true,false,false,false
Edit
as per your comment "i want only to verify if message 1 content contains string "test1" "
xDocument.Root.Elements("Session")
.Elements("Message")
.Elements("Content")
.FirstOrDefault().Value.Contains("test1");
XmlDocument advDoc=new XmlDocument();
advDoc.Load("test.xml");
XmlNodeList _ngroups = advDoc.GetElementsByTagName("Content");
foreach(XmlNode nd in _ngroups)
{
if(nd.InnerText.ToString()=="test1")
Console.WriteLine("true");
}
i want only to verify if message 1 content contains string "test1"
string pathToXmlFile = ""; // point to your xml file ...
using (StreamReader reader = File.OpenText(pathToXmlFile))
{
XDocument doc = XDocument.Load(reader); // load into XDocument
XElement idElement = doc.Root.Element("Session").Elements("Message").Elements("ID").First( item => item.Value == "1"); // since you need the message id = 1
string content = idElement.Parent.Element("Content").Value; // get the parent of this message id which is message element then navigate to its content element.
}
Hope this helps..
I'm trying to get all elements with a given value, "John", from an xml document.
Is this possible with LINQ to XML?
What I want to achieve is to replace all "John" values with "Wayne". I know this can easily be done with xslt, but I need to do this by code.
My XML:
<Root>
<Parents>
<Parent>
<Name>John</Name>
<Age>18</Age>
</Parent>
<Parent>
<Name>John</Name>
<Age>25</Age>
</Parent>
<Parent>
<Name>Peter</Name>
<Age>31</Age>
</Parent>
</Parents>
</Root>
I have tried this:
XmlDocument doc = new XmlDocument();
doc.Load(#"C:/Temp/test.xml");
var elements = doc.Elements().Where(w => w.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
You may use System.Xml.Linq.XDocument. It's more easy to work with.
XDocument doc = XDocument.Load(your file path);
var elements = doc.Descendants("Name").Where(i => i.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
doc.Save(your save file path);
Here is the output:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Parents>
<Parent>
<Name>Wayne</Name>
<Age>18</Age>
</Parent>
<Parent>
<Name>Wayne</Name>
<Age>25</Age>
</Parent>
<Parent>
<Name>Peter</Name>
<Age>31</Age>
</Parent>
</Parents>
</Root>
Here is an approach that will get all elements with the value John, regardless of what element (although only at the same level; you'd have to modify it to look at different levels too; you could use the Descendants approach described previously):
XDocument doc = XDocument.Load(#"C:\temp\test.xml");
var ns = doc.Root.GetDefaultNamespace();
var elements = doc.Element(ns + "Root").Element(ns + "Parents").Elements(ns + "Parent").Elements().Where(w => w.Value == "John");
foreach (var element in elements)
{
element.Value = "Wayne";
}
var stream = new FileStream(#"C:\temp\test.xml", FileMode.Create);
doc.Save(stream);
I am trying to parse a xml that I got as response to webservice response the xml is shown below
<GeneralSearchResponse>
<serverDetail></serverDetail>
<exceptions exceptionCount="1"></exceptions>
<clientTracking height="19" type="logo" width="106"></clientTracking>
<searchHistory></searchHistory>
<categories matchedCategoryCount="1" returnedCategoryCount="1">
<category id="0">
<name>bart</name>
<categoryURL>http://www.shopping.com/bart/products?oq=bart&linkin_id=7000610
</categoryURL>
<items matchedItemCount="1045" pageNumber="1" returnedItemCount="5">
<product id="130506037"></product>
<product id="104483377"></product>
<offer featured="false" id="tp-VCdOoO1RL6xICeRONqg==" smartBuy="false" used="false"></offer>
<offer featured="false" id="12evWWi57lddzFufngUWsg==" smartBuy="false" used="false"></offer>
<product id="96754577"></product>
</items>
<attributes matchedAttributeCount="5" returnedAttributeCount="5"></attributes>
<contentType>hybrid</contentType>
</category>
<intActualCategoryCount>4</intActualCategoryCount>
</categories>
<relatedTerms></relatedTerms>
</GeneralSearchResponse>
But when I am trying to parse using following code I am not able to get any descend or any node
XDocument xDoc = new XDocument();
xDoc = XDocument.Parse(data);
var xEle = xDoc.Root.Descendants("categories");
But xEle is not having any categories. Please let me know what is the issue??
Your XML has a default namespace - so the elements in it are in that namespace. The methods which find elements in LINQ to XML are namespace sensitive. You want:
XNamespace ns = "urn:types.partner.api.shopping.com";
XDocument xDoc = new XDocument();
xDoc = XDocument.Parse(data);
var xEle = xDoc.Root.Descendants(ns + "categories");