I have the following repetitive XML structure from which I have to create a List of an object:
<entries>
<entry>
<start>2013-10-01T00:00:00.000+02:00</start>
<end>2013-11-01T00:00:00.000+02:00</end>
<value>27.02</value>
<isExtracted>true</isExtracted>
</entry>
<entry>
<start>2013-11-01T00:00:00.000+02:00</start>
<end>2013-12-01T00:00:00.000+02:00</end>
<value>27.02</value>
<isExtracted>true</isExtracted>
</entry>
<entry>
<start>2013-12-01T00:00:00.000+02:00</start>
<end>2014-01-01T00:00:00.000+02:00</end>
<value>27.02</value>
</entry>
</entries>
I would like to extract only those elements that has the isExtracted xml tag!
What I do at the moment is the following:
var extract = xElemMaster.Elements("entries").Elements("entry")
.Where(elem => elem.Name.Equals("isExtracted"));
But I'm not getting any results out. What might have probably gone wrong?
You can use Any method
xElemMaster.Elements("entries")
.Elements("entry")
.Where(elem => elem.Elements("isExtracted").Any());
Or just try to get element and check for null:
xElemMaster.Elements("entries")
.Elements("entry")
.Where(elem => elem.Element("isExtracted") != null);
To make it more readable I would create an extension method and use it instead:
public static bool HasElement(this XElement source, string elementName)
{
return source.Element(elementName) != null;
}
xElemMaster.Elements("entries")
.Elements("entry")
.Where(elem => elem.Element.HasElement("isExtracted"));
You can search nodes and subnodes with Descendants, then use Any to check if IsExtracted exists.
var extract = xElemMaster.Descendants("entry")
.Where(w=>w.Elements("isExtracted").Any())
You might also want to check if isExtracted==True or false
var extract = x.Descendants("entry")
.Where(w => w.Elements("isExtracted").Any() && w.Element("isExtracted").Value=="true");
Related
Hi i need to extract a specific string form my xml file. How can i go about this? i have searched the internet but cant find an answer specific enough for me to understand. ^^
I want to get my SavePath string using the corresponding GameName
heres my xml file
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Game>
<entry>
<GameName>test</GameName>
<SavePath>C:\Users\allen\Downloads\GameOfLife\GameOfLife\obj\Debug\CoreCompileInputs.cache</SavePath>
<ExePath>C:\Users\allen\Downloads\GameOfLife\GameOfLife\obj\Debug\GameOfLife.exe</ExePath>
</entry>
<entry>
<GameName>test2</GameName>
<SavePath>C:\Users\allen\Downloads\GameOfLife\GameOfLife\obj\Debug\TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs</SavePath>
<ExePath>C:\Users\allen\Downloads\AdobeAIRInstaller.exe</ExePath>
</entry>
</Game>
and here's the code I've been trying to use
var xmlStr = File.ReadAllText(Properties.Resources.docname);
var str = XElement.Parse(xmlStr);
var result = str.Elements("entry")
.Where(x => x.Element("GameName").Value.Equals(SelectGame_Combobox.Text))
.Descendants("SavePath")
.ToString();
You are almost there. Just get rid of ToString() so it returns a collection of XElement (just one, of course), and then you can get it like this:
var result = str.Elements("entry").
Where(x => x.Element("GameName").Value.Equals(search)).Descendants("SavePath");
string value = result.First().Value;
Here is a snippet of my XML (with only a few entries whereas there are actually around a thousand).
<?xml version="1.0" encoding="UTF-8"?>
<information_code version="3.5">
<entry code="000" title="Function, data for plans and description"/>
<entry code="001" title="Title page"/>
<entry code="002" title="List of pages or data modules"/>
<entry code="003" title="Change records and highlights"/>
<entry code="004" title="Access illustration"/>
</information_code>
What I need to do is match the 'code' attribute with a value that I pass into my query, then return the value of the 'title' attribute. It really shouldn't be hard but I'm going around in circles.
Here's where I currently am, but it always gets caught without matching. Obviously something wrong with my query.
private string getInfoName(string infoCode)
{
XDocument mydoc = XDocument.Load(GlobalVars.pathToInfoCodes);
string name = string.Empty;
try
{
var entry = mydoc
.Elements("entry")
.Where(e => e.Attribute("code").Value == infoCode)
.Single();
name = entry.Attribute("title").Value;
}
catch
{
MessageBox.Show("Info code not recognised: " + infoCode);
}
return name;
}
The problem is that when you use Elements it searches only in the level you are currently at, which in this point is the <information_code> - so there are no <entry> elements there.
You can use .Element("information_code").Elements("entry") or use instead .Descendants:
string wantedCode = "001";
var title = XDocument.Load(GlobalVars.pathToInfoCodes)
.Descendants("entry")
.Where(e => e.Attribute("code")?.Value == wantedCode)
.Select(e => e.Attribute("title").Value).FirstOrDefault();
You can also do it with the query syntax. Might look nicer:
var title = (from e in XDocument.Load("data.xml").Descendants("entry")
where e.Attribute("code")?.Value == wantedCode
select e.Attribute("title")?.Value).FirstOrDefault();
Note that the ?. syntax is C# 6.0 Null Propagation. If you are with an earlier C# version then you will have to store the attribute in a variable, check that it is not null and only then access .Value
I have the following xml:
<?xml version="1.0" encoding="utf-8" ?>
<layout>
<menu name="Employees" url="Employees.aspx" admin="0">
</menu>
<menu name="Projects" url="Projects.aspx" admin="1">
</menu>
<menu name="Cases" url="Cases.aspx" admin="1">
</menu>
<menu name="CaseView" url="CaseView.aspx" admin="1" hidden="1" parent="Projects">
</menu>
<menu name="Management" url="" admin="1">
<item name="Groups" url="Groups.aspx" admin="1" parent="Management"/>
<item name="Statuses" url="Statuses.aspx" admin="1"/>
</menu>
</layout>
Here I have CaseView and Groups that both have a 'parent' attribute.
Currently I iterate like this:
IEnumerable<XElement> menus =
doc.Element("layout").Elements();
foreach (var menu in menus)
{
string name = menu.Attribute("name").Value;
string active = "";
string url = menu.Attribute("url").Value;
if(activePage == url)
{
active = "class=\"active\"";
}
...
What I want is:
if(activePage == url || ActiveIsChildOf(name, activePage))
{
active = "class=\"active\"";
}
Essentially this method needs to find if an element with activePage as its url attribute exists. If it does, see if it has a parent attribute; if it does, check if the parent == name.
Is there some way to find an element by attribute or something?
ex:
XElement e = doc.GetByAttribute("url",activePage)
Thanks
Since you are using Linq to XML, you can use Descendants method - it returns all child elements, not just the direct children. After that, you can use LINQ to filter the results.
XDocument doc;
string activePage;
var activeMenu = doc.Descendants("menu")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
You might need to check if o.Attribute("url") does not return null (it does when the attribute does not exist) if you cannot guarantee that the source XML does not have such attribute for all menu elements.
You can also skip the argument to Descendants() to check all elements - in your sample data that would allow you to check both menu and item elements. For example:
var activeMenu = doc.Descendants()
.Where(o => o.Name == "menu" || o.Name == "item")
.FirstOrDefault(o => o.Attribute("url").Value == activePage);
If xpath is too cryptic, you can use LINQ:
IEnumerable<XElement> hits =
(from el in XMLDoc.root.Elements("item")
where (string)el.Attribute("url") == activePage
select el);
or like this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => ((string)e.Attribute("url")) == activePage);
if(null != xele )
{
// do something with it
}
And you probably want it case-insensitive:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements("item").FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
If you want both menu and item, use this:
XElement xml = XElement.Load(file);
XElement xele = xml.Elements().FirstOrDefault(e => StringComparer.OrdinalIgnoreCase.Equals((string)e.Attribute("url") , activePage));
if(null != xele )
{
// do something with it
}
You can simply use xPath. It's a query language for XML.
You can formulate something like this :
var xDoc = new XmlDocument();
xDoc.Load("XmlFile.xml");
//Fetch your node here
XmlNode = xDoc.SelectSingleNode(/layout/menu[#url='activepage'][1]);
It returns a set of node and the index 1 is to get the first node of the given set.
You can always use xDoc.SelectNodes if you want all the matching nodes.
Since you are using LINQ you can simply include System.Xml.XPath and select nodes with XPathSelectElement or XPathSelectElements.
You can do that with XPath:
doc.SelectNodes("//*[#url='" + activePage + "']")
It will return all document items that have activePage as url attribute.
A case insensitive search example, converting xml to a dictionary:
Dim expandos = XDocument.Parse(Request("Xml")).Root.Elements.Select(
Function(e)
Dim expando As Object = New ExpandoObject,
dic = e.Attributes.ToDictionary(Function(a) a.Name.LocalName, Function(a) a.Value,
StringComparer.InvariantCultureIgnoreCase)
expando.PedidoId = dic("PedidoId")
expando.FichaTecnicaModeloId = dic("FichaTecnicaModeloId")
expando.Comodo = dic("Comodo")
expando.Cliente = dic("Cliente")
Return expando
End Function)
I stored the xml structure in xml string like abcd variable.test1,test2,test3 are parts of the xml sructure.how to get suffix values like 1,2,3 from test1,test2,test3?
string abcd="<xmlstruct>
<test1>
<name>testname1</name>
<address>testaddress1</address>
<subject>testsub1<subject>
</test1>
<test2>
<name>testname2</name>
<address>testaddress2</address>
<subject>testsub2<subject>
</test2>
<test3>
<name>testname3</name>
<address>testaddress3</address>
<subject>testsub3<subject>
</test3>
</xmlstruct>";
Ideally, don't structure your XML like that in the first place. It's not a good use of element names. It would be better to use:
<test id="1">
...
</test>
<test id="2">
...
</test>
If these are the result of having separate variables in your original classes, that suggests the variables should probably be a single collection instead.
If you really want to find them though, you could use something like this:
IEnumerable<string> ListSuffixes(XElement container, XName prefix)
{
string localPrefix = prefix.Name.LocalName;
var elements = container.Elements()
.Where(x => x.Name.Namespace == prefix.Name.Namespace
&& x.Name.LocalName
.StartsWith(localPrefix));
foreach (var element in elements)
{
yield return element.Name.LocalName.Substring(localPrefix.Length);
}
}
I'm not entirely sure what you are trying to achieve, but this isn't really how XML is normally used.
To obtain the suffixes (1, 2, 3) from a piece of XML that looks like the above then you could parse the XML, select all children of the xmlstruct element and then use string manipulation.
However an alternative schema would probably be a better idea, like storing the suffixes separately as attributes
<xmlstruct>
<test Suffix="1">
<name>testname1</name>
<address>testaddress1</address>
<subject>testsub1<subject>
</test>
<test Suffix="2">
<name>testname2</name>
<address>testaddress2</address>
<subject>testsub2<subject>
</test>
<test Suffix="3">
<name>testname3</name>
<address>testaddress3</address>
<subject>testsub3<subject>
</test>
</xmlstruct>
Element names shouldn't really be dynamic, the list of allowed element names for a given element should normally belong to a fixed (finite) list
You can try this :
Integer.parseInt(s.replaceAll("[\\D]", ""))
This will also remove non-digits inbetween digits, so "test1test1x" becomes 11.
This works:
var suffices =
XDocument
.Parse(abcd)
.Element("xmlstruct")
.Elements()
.Where(xe => xe.Name.ToString().StartsWith("test"))
.Select(xe => int.Parse(xe.Name.ToString().Substring(4)));
It returns:
I'm working with an existing XML document which has a structure (in part) like so:
<Group>
<Entry>
<Name> Bob </Name>
<ID> 1 </ID>
</Entry>
<Entry>
<Name> Larry </Name>
</Entry>
</Group>
I'm using LINQ to XML to query the XDocument to retrieve all these entries as follows:
var items = from g in xDocument.Root.Descendants("Group").Elements("Entry")
select new
{
name = (string)g.element("Name").Value,
id = g.Elements("ID").Count() > 0 ? (string)g.Element("ID").Value : "none"
};
The "ID" elements aren't always there and so my solution to this was the Count() jazz above. But I'm wondering if someone has a better way to do this. I'm still getting comfortable with this new stuff and I suspect that there may be a better way to do this than how I'm currently doing it.
Is there a better/more preferred way to do what I want?
XElement actually has interesting explicit conversion operators that do the right thing in this case.
So, you rarely actually need to access the .Value property.
This is all you need for your projection:
var items =
from g in xDocument.Root.Descendants("Group").Elements("Entry")
select new
{
name = (string) g.Element("Name"),
id = (string) g.Element("ID") ?? "none",
};
And if you'd prefer to use the value of ID as an integer in your anonymous type:
var items =
from g in xDocument.Root.Descendants("Group").Elements("Entry")
select new
{
name = (string) g.Element("Name"),
id = (int?) g.Element("ID"),
};
In a similar situation I used an extension method:
public static string OptionalElement(this XElement actionElement, string elementName)
{
var element = actionElement.Element(elementName);
return (element != null) ? element.Value : null;
}
usage:
id = g.OptionalElement("ID") ?? "none"
How about:
var items = from g in xDocument.Root.Descendants("Group").Elements("Entry")
let idEl = g.Element("ID")
select new
{
name = (string)g.element("Name").Value,
id = idEl == null ? "none" : idEl.Value;
};
if this barfs, then FirstOrDefault() etc might be useful, else just use an extension method (as already suggested).