#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: Вы не должны редактировать ответы, чтобы эффективно запрашивать разъяснения. Мой ответ отвечает на заданный вами вопрос — если есть еще что-то, что вы не указали, пожалуйста, задайте новый вопрос. Вы можете оставить комментарий здесь, ссылаясь на новый вопрос.