How do I write a Lambda statement for this Linq to XML function?
I am working with this XSD file. The portion of the XML that is relevant to this question is here:
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="https://wsmrc2vger.wsmr.army.mil/rcc/manuals/106-11"
targetNamespace="https://wsmrc2vger.wsmr.army.mil/rcc/manuals/106-11"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="Tmats">
<xs:complexType>
<xs:sequence>
<xs:annotation>
<xs:documentation>TMATS G Group</xs:documentation>
</xs:annotation>
<xs:element name="ProgramName" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>PN</xs:documentation>
</xs:annotation>
</xs:element>
To get the documentation
value for a given xs:element
, I have this small function, which recursively walks the descendant nodes until it finds the documentation
element:
public string GetCode(XElement e)
{
foreach (var s in e.Elements())
{
// If we hit an intervening element,开发者_JAVA百科 bail out.
if (s.Name.ToString().Contains("element"))
return "";
if (s.Name.ToString().Contains("annotation"))
{
// I'll explain this loop in a moment.
foreach (var t in s.Elements())
{
if (t.Name.ToString().Contains("documentation"))
return t.Value;
}
}
else
return GetCode(s);
}
return "";
}
So far so good. The unit test looks like this:
[TestMethod()]
public void GetCodeTest()
{
string path = @"C:\Documents and Settings\harvey robert\Desktop\Tmats.xsd";
IEnumerable<XElement> elements =
from e in XElement.Load(path).Elements()
select e;
TmatsDictionary target = new TmatsDictionary();
XElement x = elements.First();
string actual = target.GetCode(x);
Assert.AreEqual("TMATS G Group", actual);
}
Which passes. Now I want to extend the test by adding an additional case, like this:
XElement z = elements.DescendantsAndSelf()
.First(y => y.Attribute("name")
.ToString().Contains("ProgramName"));
actual = target.GetCode(z);
Assert.AreEqual("PN", actual);
...But this fails due to a null object reference (most likely y.Attribute("name")
).
Did you see the loop in the function above that I commented?
// I'll explain this loop in a moment.
foreach (var t in s.Elements())
{
if (t.Name.ToString().Contains("documentation"))
return t.Value;
}
It's written that way because I can't figure out how to express the condition in a Lambda statement that works.
Any suggestions?
You need to use namespaces:
XNamespace ns = "https://wsmrc2vger.wsmr.army.mil/rcc/manuals/106-11";
XElement z = elements.DescendantsAndSelf()
.First(y => y.Attribute(ns + "name")
.Value.Contains("ProgramName"));
Try this
elements.DescendantsAndSelf().
First(y => y.Attribute(XName.Get("name", "http://www.w3.org/2001/XMLSchema")));
Your problem is that y.Attribute("name").ToString().Contains("ProgramName")
will fail on every element that doesn't contain a "name" attribute. You need something like this:
y.Attribute("name") != null &&
y.Attribute("name").ToString().Contains("ProgramName");
If you expect all <element>
elements to contain a name
attribute, you can ignore the null check and do this:
XElement z = elements.DescendantsAndSelf(
"{http://www.w3.org/2001/XMLSchema}element")
.First(y => y.Attribute("name")
.ToString().Contains("ProgramName"));
EDIT: Note that I added the expanded name to include the namespace URL. See if that works.
Here is the code that works.
Notice the call to the GetName()
method in the DescendantsAndSelf()
call. This returns a properly formatted URI-prefixed name of the form {http://www.w3.org/2001/XMLSchema}element
, which will match correctly to the xs:element
names.
The result is that DescendantsAndSelf()
returns only those elements having the name xs:element
, all of which have attributes associated with them (so there is no chance of a null reference error when referencing the Attributes
collection).
[TestMethod()]
public void GetCodeTest()
{
string path = @"C:\TestArea\Tmats_09-2010.xml";
IEnumerable<XElement> elements =
from e in XElement.Load(path).Elements()
select e;
TmatsDictionary target = new TmatsDictionary();
XNamespace ns = "http://www.w3.org/2001/XMLSchema";
XElement z = elements.DescendantsAndSelf(ns.GetName("element"))
.First(y => y.Attribute("name")
.Value.Equals("ProgramName"));
actual = target.GetCode(z);
Assert.AreEqual("PN", actual);
}
精彩评论