Reputation: 1321
I have a collection of MODS records that looks like this:
<modsCollection>
<mods [namespaces etc] >
<genre authority="diva" type="contentType" lang="eng">Other academic</genre>
<genre authority="diva" type="contentType" lang="swe">Övrigt vetenskapligt</genre>
<name type="personal">
<namePart type="family">Svensson</namePart>
<namePart type="given">Sven</namePart>
<namePart type="date">1880-</namePart>
<role>
<roleTerm type="code" authority="marcrelator">aut</roleTerm>
</role>
<affiliation>Stockholms universitet, institutionen institutionen</affiliation>
</name>
[...]
</mods>
<mods/>
<mods/>
</modsCollection>
My LINQ query to search the colloction for records affiliated with a certain person with a certain role looks like this:
XElement[] hits = (from record in x.Root.Elements(modsV3 + "mods").Elements(modsV3 + "name")
from r1 in record.Elements(modsV3+"namePart")
where
r1.HasAttributes &&
r1.Attribute("type").Value == "family" &&
r1.Value == familyName
from r2 in record.Elements(modsV3 + "namePart")
where
r2.HasAttributes &&
r2.Attribute("type").Value == "given" &&
r2.Value == givenName
from r3 in record.Elements(modsV3 + "role").Elements(modsV3+"roleTerm")
where
r3.HasAttributes &&
r3.Attribute("type").Value == "code" &&
r3.Value == "aut"
select r1.Parent.Parent).ToArray<XElement>();
I think this query could be better written. How?
Upvotes: 2
Views: 230
Reputation:
I would use the Extension method syntax, and make Linq filter methods like below:
private static XElement[] GetHits(XDocument x, string modsV3, string givenName, string familyName)
{
return x.Root.Elements(modsV3 + "mods")
.MatchNamePart("given", givenName)
.MatchNamePart("family", familyName).ToArray();
}
private static string modsV3 = "whatever";
private static IEnumerable<XElement> MatchNamePart(this IEnumerable<XElement> records, string type, string givenName)
{
return records.Where(rec => rec.Element(modsV3 + "name").
Elements(modsV3 + "namePart").Any(r1 => HasAttrib(r1, type, givenName)));
}
private static bool HasAttrib(XElement element, string attribName, string value)
{
return element.HasAttributes &&
element.Attribute("type").Value == attribName &&
element.Value == value;
}
This only does the name matching. But you can use these methods as buildingblocks. You can reuse the name part matching wherever you query this type of document, so the non-reusable portion is small. The second half of the query can be derived from this example.
Upvotes: 2
Reputation: 2337
I know you want to use only LINQ, but maybe a mix with XPath simplifies your code:
XElement[] hits = (from record in config.Elements(modsV3 + "mods").Elements(modsV3 + "name")
where record.XPathSelectElement(string.Format("./{0}namePart[@type='family' and .='{1}']", modsV3, familyName)) != null &&
record.XPathSelectElement(string.Format("./{0}namePart[@type='given' and .='{1}']", modsV3, givenName)) != null &&
record.XPathSelectElement(string.Format("./{0}role/{0}roleTerm[@type='code' and .='aut']", modsV3)) != null
select record.Parent).ToArray<XElement>();
Upvotes: 2