OptionWriteEmptyNodes нарушает объявление XML с помощью HtmlAgilityPack
Вот супер простой код, который у меня есть:
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.OptionWriteEmptyNodes = true;
htmlDoc.Load("sourcefilepath");
htmlDoc.Save("destfilepath", Encoding.UTF8);
Входные данные:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
<link rel="stylesheet" href="main.css" type="text/css"/>
</head>
<body>lots of text here, obviously not relevant to this problem</body>
</html>
Выход:
<?xml version="1.0" encoding="UTF-8" />
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<link rel="stylesheet" href="main.css" type="text/css" />
</head>
<body>lots of text here, obviously not relevant to this problem</body>
</html>
Вы можете видеть, что в первой строке есть ошибка: /> вместо?> Это происходит, если я устанавливаю OptionWriteEmptyNodes в истинное значение. Для него установлено значение true, поскольку в противном случае мета / теги ссылок (и некоторые другие в теле документа) не будут закрыты.
Кто-нибудь знает, как решить эту проблему?
3 ответа
Похоже, ошибка. Вы должны сообщить об этом на http://htmlagilitypack.codeplex.com/.
Тем не менее, вы можете обойти эту ошибку следующим образом:
HtmlNode.ElementsFlags.Remove("meta");
HtmlNode.ElementsFlags.Remove("link");
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.Load("sourcefilepath");
htmlDoc.Save("destfilepath", Encoding.UTF8);
Просто удалите флаги из meta
& link
теги, которые инструктируют Html Agility Pack не закрывать их автоматически и не устанавливают OptionWriteEmptyNodes
в true
,
Это произведет это (заметьте, что это немного отличается):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"></meta>
<link rel="stylesheet" href="main.css" type="text/css"></link>
</head>
<body>lots of text here, obviously not relevant to this problem</body>
</html>
Удалось сделать другой способ обойти эту проблему. Это работает немного лучше в моем случае, чем выше. По сути, мы заменяем первый дочерний элемент DocumentNode, который является объявлением xml (обратите внимание, что входные данные должны содержать объявление xml, в моем случае это 100%).
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.OptionWriteEmptyNodes = true;
htmlDoc.Load("sourcepath");
var newNodeStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
var newNode = HtmlNode.CreateNode(newNodeStr);
htmlDoc.DocumentNode.ReplaceChild(newNode, htmlDoc.DocumentNode.FirstChild);
htmlDoc.Save("destpath", Encoding.UTF8);
Обратите внимание, что обходной путь Саймона тоже работает, поэтому возьмите тот, который лучше соответствует вашему сценарию.
Мои страницы также имеют <br/>
теги в них и удаление htmlDoc.OptionWriteEmptyNodes = true;
ломает те, заменяя их <br>
, Я нашел подход, похожий на ответ Алекса, но немного более общий, чтобы сохранить большинство исходных значений, и не полагаться на то, что на вашей странице всегда будет тег xml:
HtmlDocument doc= new HtmlDocument();
doc.OptionWriteEmptyNodes = true;
doc.Load("pathToFile");
if (doc.DocumentNode.FirstChild.OriginalName.Equals("?xml"))
{
var fixedOuterHtml = doc.DocumentNode.FirstChild.OuterHtml.Replace('/', '?');
var newNode = HtmlNode.CreateNode(fixedOuterHtml);
doc.DocumentNode.ReplaceChild(newNode, doc.DocumentNode.FirstChild);
}