Как мне узнать элементы перед определенным элементом в XML в c #?

#c# #.net #linq

#c# #.net #linq

Вопрос:

У меня есть XML в следующем формате:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE repub SYSTEM "C:repubRepub_V1.dtd">
<?xml-stylesheet href="C:repubrepub.xsl" type="text/xsl"?>
<repubold>
    <head>
        <title>xxx</title>
    </head>
    <body>
        <sec>
            <title>First Title</title>
            <break name="1-1"/>
            <pps>This is an invalid text.</pps>
            <h1>
                <page num="1"/>First Heading
            </h1>
            <bl>This is another text</bl>
            <fig>
                <img src="images/img_1-1.jpg" alt=""/>
                <fc>This is a caption</fc>
            </fig>
            <p>
                <bold>This</bold> again
                <br/> is
                <br/>
                <bold> a 
                    <br/>paragraph
                </bold>
            </p>
        </sec>
        <sec>
            <title>Second Title</title>
            <break name="2-1"/>
            <h1>
                <page num="1"/>Second Heading
            </h1>
            <bl>This is another text</bl>
            <fig>
                <img src="images/img_2-1.jpg" alt=""/>
                <fc>This is a caption</fc>
                <cr>This is a credit</cr>
            </fig>
            <p>This is a paragraph</p>
        </sec>
        <sec>
            <title>First Title</title>
            <break name="3-1"/>
            <h1>
                <page num="1"/>Third Heading
            </h1>
            <bl>This is another text</bl>
            <fig>
                <img src="images/img_3-1.jpg" alt=""/>
                <fc>This is a caption</fc>
            </fig>
            <p>This is a paragraph</p>
        </sec>
        <sec>
            <title>Third Title</title>
            <break name="4-1"/>
            <h1>
                <page num="1"/>Fourth Heading
            </h1>
            <bl>This is another text</bl>
            <p>This is a paragraph</p>
            <fig>
                <img src="images/img_4-1.jpg" alt=""/>
                <fc>This is a caption</fc>
                <cr>This is a credit</cr>
            </fig>
            <break name="5-1"/>
            <h1>
                <page num="1"/>Fifth Heading
            </h1>
            <bl>This is another text</bl>
            <fig>
                <img src="images/img_5-1.jpg" alt=""/>
                <fc>This is a caption</fc>
                <cr>This is a credit</cr>
            </fig>
            <p>This is a paragraph</p>
        </sec>
    </body>
</repubold>
  

В этом случае за всеми <break> тегами следует <h1> . Итак, я хочу проверить элементы перед <h1> , если таковые имеются. Если это не так <psf> , то будет отображаться ошибка.
Потому что я хочу, чтобы это <psf> был единственный приемлемый тег между <break> и <h1> . Это может быть <psf> или ничего, но если есть какой-либо другой <xyz> тег, то он покажет ошибку.

Пожалуйста, помогите.

Я пробовал это, но код не работает:

 var pagetag = xdoc.Descendants("break").Descendants("h1")
.Where(br => br.ElementsBeforeSelf("h1") != new XElement("psf") ||                                                                 
br.ElementsBeforeSelf("h1") != new XElement("break"))
.Select(br => br.Attribute("name").Value.Trim())
.Aggregate((a, b) => a   ", "   b);

MessageBox.Show("The following articles have invalid tags before <h1>: "   pagetag);
  

Комментарии:

1. Что именно не работает?

2. @Stefan — Я обновил код. Результат должен был быть 1-1, но я получаю исключение Sequence has no elements .

3. Я не совсем понимаю, что вы пытаетесь сделать — в вашем образце XML нет никаких элементов psf, что затрудняет понимание.

4. @JonSkeet — В принципе, в общем случае не должно быть элементов после <break> и до <h1> . Но единственное исключение, которое у меня может быть, это <psf> . Если после <break> и до <h1> присутствуют какие-либо другие элементы, то это будет ошибкой. Итак, в моем вводе <break name="1-1"> ошибка, поскольку он содержит элемент между <break> и <h1> , а это не <psf> .

5. Я добавил ответ, но было бы полезно, чтобы ваш XML включал пример этого. В настоящее время в XML-документе много не особо релевантных, но в нем нет примера всего, что имеет значение.

Ответ №1:

Первая проблема заключается в том, что ElementsBeforeSelf() возвращается последовательность элементов, но вы проверяете, равна ли эта последовательность единице XElement , и сравниваете их по ссылке с помощью != .

Вы также запрашиваете потомков break элементов — и их нет. Я думаю, вам просто нужны все h1 элементы.

Чтобы уточнить ваше требование, я думаю, что вы пытаетесь найти все h1 элементы, где последний элемент перед h1 не является ни break ни psf . Для каждого из этих элементов вы хотите найти последний break элемент перед h1 (если он есть) и сообщить name атрибут.

Предполагая, что это так, вот некоторый код, который, как я полагаю, делает то, что вы хотите, с комментариями, объясняющими это:

 using System;
using System.Linq;
using System.Xml.Linq;

public class Test
{
    public static void Main()
    {
        var xdoc = XDocument.Load("test.xml");
        XName brName = "break";
        XName psfName = "psf";

        var invalidNames = 
            from h1 in xdoc.Descendants("h1")
            // Find the last sibling element before the h1
            let previous = h1.ElementsBeforeSelf().LastOrDefault()
            // It's invalid if there isn't a previous element, or it has
            // a name other than break or psf
            where previous?.Name != brName amp;amp; previous?.Name != psfName
            // Get the name to report, handling the case where there's
            // no previous break or no "name" attribute
            select ((string) h1.ElementsBeforeSelf(brName).LastOrDefault()?.Attribute("name")) ?? "(no named break)";

        Console.WriteLine(string.Join(", ", invalidNames));
    }
}
  

У него есть небольшой недостаток, заключающийся в том, что если <h1> недопустимо, но не имеет непосредственного <break> предшественника, он будет оглядываться назад до более раннего элемента, чтобы найти имя… так что, если вы удалите, например, <break name="5-1"/> элемент, он сообщит, что имя «4-1» недопустимо, поскольку это последний break элемент перед h1 , который был после 5-1. Я не знаю, насколько это важно для вас.

Комментарии:

1. Здравствуйте! Я отредактировал ваш комментарий и предоставил свой код. Ваш код начал показывать все <break> теги в XML, а это не требуется. Пожалуйста, просмотрите комментарий и помогите мне.

2. @PrimoChalice: Вы не должны редактировать ответы, чтобы эффективно запрашивать разъяснения. Мой ответ отвечает на заданный вами вопрос — если есть еще что-то, что вы не указали, пожалуйста, задайте новый вопрос. Вы можете оставить комментарий здесь, ссылаясь на новый вопрос.