Разбор XHTML со встроенными тегами - PullRequest
0 голосов
/ 16 апреля 2010

Я пытаюсь проанализировать документ XHTML, используя TBXML на iPhone (хотя я был бы рад использовать libxml2 или NSXMLParser, если это будет проще). Мне нужно извлечь содержимое тела в виде серии абзацев и сохранить встроенные теги, например:

<!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" xml:lang="en">
    <head>
       <title>Title</title>
       <link rel="stylesheet" href="css/style.css" type="text/css"/>
       <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
    </head>
    <body>
       <div class="body">
          <div>
             <h3>Title</h3>
             <p>Paragraph with <em>inline</em> tags</p>
             <img src="image.png" />
          </div>
       </div>
    </body>
</html>

Мне нужно извлечь абзац, но сохранить содержание <em>inline</em> с абзацем, все мои тесты до сих пор извлекали его как подэлемент, и я не знал точно, где он помещается в абзаце.

Может кто-нибудь предложить способ сделать это?

Спасибо.

1 Ответ

1 голос
/ 17 апреля 2010

Предположение 1. Вас интересуют только данные в элементе p (абзаце) и то, что вы используете NSXMLParser.

Предположение 2. Вы хотите сохранить любой элемент внутри p без изменений.

Стратегия, которую вы хотите использовать, состоит в том, чтобы создать конечный автомат для вашего анализатора, чтобы он знал, когда ему нужно сохранять данные, а когда игнорировать данные по мере их получения.

Настройте NSXMLParser delegate, используя образец кода от Apple. Вашему делегату понадобится ивар BOOL inParagraph для отслеживания того, когда данные будут сохранены или отброшены. Начальное значение inParagaph составляет NO. Когда ваш делегат получит сообщение parser:didStartElement:namespaceURI:qualifiedName:attributes:, if ([element isEqual:@"p"]) очистите переменную receivedData и установите inParagraph = YES

РЕДАКТИРОВАТЬ: ReceiveData является NSMutableString. Исправлены примеры кода

В этот момент ваш parser delegate хочет сохранить полученные данные.

Когда parser delegate получает сообщение parser:foundCharacters:, добавьте строку к receivedData, как в примере кода.

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if (inParagraph) [receivedData appendString:string];
}

Когда синтаксический анализатор встречает встроенный элемент, делегат снова получит parser:didStartElement:namespaceURI:qualifiedName:attributes:. Это когда важна переменная состояния inParagraph. Синтаксический анализатор не получит вмещающие символы «<» и «>» элемента, поэтому вам придется заключить elementName в символы «<» и «>» и добавить к receivedData. Что-то вроде

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{ if (inParagraph) 
    {
        NSString *inlineElementName = [NSString stringWithFormat:@"<%@>", elementName];
        [receivedData appendString:inlineElementName];
    }
....
}

Когда parser delegate получает сообщение parser:didEndElement:namespaceURI:qualifiedName:, он проверяет, находится ли оно в элементе "p", if (inParagraph && ![elementName isEqual:@"p"], закрывает встроенный элемент. if ([elementName isEqual:@"p"]) добавьте содержимое receivedData к NSMutableArray, содержащему ваши абзацы.

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
   if (inParagraph)
        {
             if (![elementName isEqual:@"p"])
                 { 
                      NSString *inlineElementName = [NSString stringWithFormat:@"</%@>", elementName];
                     [receivedData appendString:inlineElementName];             
                 } else { // received closing </p> tag add receivedData to the paragraph array
                          [paragraphsArray addObject:[receivedData copy]];
                          [self setInParagraph:NO];
                         }
                 }
       }
}
...