Как мне написать лямбда-оператор для этой функции Linq to XML? - PullRequest
1 голос
/ 26 октября 2010

Я работаю с этим XSD-файлом. Часть XML, относящаяся к этому вопросу, находится здесь:

<?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>

Чтобы получить значение documentation для данногоxs:element, у меня есть эта маленькая функция, которая рекурсивно обходит узлы-потомки, пока не найдет элемент documentation:

public string GetCode(XElement e)
{
    foreach (var s in e.Elements())
    {
        // If we hit an intervening element, 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 "";
}

Пока все хорошо.Модульный тест выглядит так:

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

Что проходит.Теперь я хочу расширить тест, добавив дополнительный регистр, например:

    XElement z = elements.DescendantsAndSelf()
                         .First(y => y.Attribute("name")
                         .ToString().Contains("ProgramName"));

    actual = target.GetCode(z);
    Assert.AreEqual("PN", actual);

... Но это не удалось из-за пустой ссылки на объект (наиболее вероятно y.Attribute("name")).

Вы видели цикл в функции выше, которую я прокомментировал?

// I'll explain this loop in a moment.
foreach (var t in s.Elements())
{
    if (t.Name.ToString().Contains("documentation"))
        return t.Value;
}

Это написано так, потому что Я не могу понять, как выразить условие вЛямбда-оператор, который работает.

Есть предложения?

Ответы [ 4 ]

1 голос
/ 26 октября 2010

Попробуйте это

elements.DescendantsAndSelf().
                First(y => y.Attribute(XName.Get("name", "http://www.w3.org/2001/XMLSchema")));
1 голос
/ 26 октября 2010

Ваша проблема в том, что y.Attribute("name").ToString().Contains("ProgramName") не удастся выполнить для каждого элемента, который не содержит атрибута "name".Вам нужно что-то вроде этого:

y.Attribute("name") != null &&
y.Attribute("name").ToString().Contains("ProgramName");

Если вы ожидаете, что все элементы <element> содержат атрибут name, вы можете проигнорировать нулевую проверку и сделать это:

XElement z = elements.DescendantsAndSelf(
                         "{http://www.w3.org/2001/XMLSchema}element")
                     .First(y => y.Attribute("name") 
                     .ToString().Contains("ProgramName"));

РЕДАКТИРОВАТЬ: Обратите внимание, что я добавил расширенное имя, чтобы включить URL-адрес пространства имен.Посмотрите, работает ли это.

1 голос
/ 26 октября 2010

Вам необходимо использовать пространства имен:

XNamespace ns = "https://wsmrc2vger.wsmr.army.mil/rcc/manuals/106-11";
XElement z = elements.DescendantsAndSelf()
                     .First(y => y.Attribute(ns + "name")
                         .Value.Contains("ProgramName"));
0 голосов
/ 26 октября 2010

Вот код, который работает.

Обратите внимание на вызов метода GetName() в вызове DescendantsAndSelf(). Это возвращает правильно отформатированное имя с префиксом URI в форме {http://www.w3.org/2001/XMLSchema}element, которое будет правильно соответствовать именам xs:element.

В результате DescendantsAndSelf() возвращает только те элементы, которые имеют имя xs:element, у всех из которых есть атрибуты, связанные с ними (поэтому при ссылке на коллекцию Attributes вероятность ошибки нулевой ссылки отсутствует.

[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);
}
...