开发者

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);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜