AngleSharp. Проблема замены элемента через OuterHtml (< > &lt; &gt;)

299
26 ноября 2016, 19:44

Необходимо заменить в html-странице все теги <iframe> на соответствующие теги <video>. Пытаюсь сделать это с помощью AngleSharp.

private static void SubstituteVideo(IHtmlDocument doc, List<string> videoPaths)
{
var selector = @"iframe[allowfullscreen]";
var elements = doc.QuerySelectorAll(selector);
for (int i = 0; i < elements.Length && i < videoPaths.Count; i++)
{
    IElement element = elements[i];
    element.OuterHtml = @"
        <video height=""405"" width=""720"" style=""display: block; margin: 0 auto;"" preload controls>
            <source src=""" + videoPaths[i] + @""" type=""video/mp4"">
        </video>";
}

}

По какой-то причине при присваивании свойству OuterHtml в строке все угловые скобки заменяются на &lt; &gt;. Получается что-то вроде этого

&lt;video height="405" width="720" style="display: block; margin: 0 auto;" preload controls&gt;
    &lt;source src="Z:\path\to\video" type="video/mp4"&gt;
&lt;/video&gt;

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

private static void DownloadAndSubstituteAudio(string currentDir, IHtmlDocument doc)
{
    var audioLinkSelector = "audio a";
    var audioLinkTagList = doc.QuerySelectorAll(audioLinkSelector);
    Directory.CreateDirectory(currentDir + "\\audio");
    foreach (AngleSharp.Dom.IElement linkTag in audioLinkTagList)
    {
        var link = linkTag.GetAttribute("href");
        var audioName = link.Substring(link.LastIndexOf('/') + 1);
        var audioPath = currentDir + @"\audio\" + audioName;
        var webClient = new WebClient();
        webClient.DownloadFile(link, audioPath);
        var upperDiv = linkTag.ParentElement.ParentElement.ParentElement.ParentElement;
        upperDiv.OuterHtml = @"
            <audio src=""audio/" + audioName + @""" style=""width: 100%;"" controls preload>
                <source type=""audio/mpeg"" src=""audio/" + audioName + @"""><a href=""audio/" + audioName + @""">" + audioName + @"</a>
            </audio>";
    }
}

Результат работы

<audio src="audio/The-Beatles-The-Beatles-Ob-La-Di-Ob-La-Da.mp3" style="width: 100%;" controls="" preload="">
    <source type="audio/mpeg" src="audio/The-Beatles-The-Beatles-Ob-La-Di-Ob-La-Da.mp3"><a href="audio/The-Beatles-The-Beatles-Ob-La-Di-Ob-La-Da.mp3">The-Beatles-The-Beatles-Ob-La-Di-Ob-La-Da.mp3</a>
</audio>
Answer 1

Попробуйте явно распарсить текст тега video и добавить полученный элемент к родителю элемента iframe (сеттер свойства OuterHtml делает примерно то же самое, только почему-то передает в ParseFragment контекстом сам iframe, а не его родителя, поэтому video превращается не в IHtmlElement, а в IText с экранированными символами - возможно, это баг в AngleSharp):

private static void SubstituteVideo(IHtmlDocument doc, List<string> videoPaths)
{
    var selector = @"iframe[allowfullscreen]";
    var elements = doc.QuerySelectorAll(selector);
    var parser = new HtmlParser();
    for (int i = 0; i < elements.Length && i < videoPaths.Count; i++)
    {
        IElement element = elements[i];
        var videoText = @"
<video height=""405"" width=""720"" style=""display: block; margin: 0 auto;"" preload controls>
    <source src=""" + videoPaths[i] + @""" type=""video/mp4"">
</video>";
        var videoElement = parser
            .ParseFragment(videoText, element.ParentElement)
            .OfType<IHtmlElement>()
            .First();
        element.Parent.InsertBefore(videoElement, element);
        element.Remove();
    }
}