я думаю, что считал каждую веб-страницу, касающуюся этой проблемы, но я все еще не могу найти решение ее, таким образом, здесь я.
У меня есть веб-страница HTML, которая не находится под моим контролем, и я должен проанализировать его из своего приложения для iPhone. Здесь это - образец веб-страницы, о которой я говорю:
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</HEAD>
<BODY>
<LI class="bye bye" rel="hello 1">
<H5 class="onlytext">
<A name="morning_part">morning</A>
</H5>
<DIV class="mydiv">
<SPAN class="myclass">something about you</SPAN>
<SPAN class="anotherclass">
<A href="http://www.google.it">Bye Bye è un saluto</A>
</SPAN>
</DIV>
</LI>
</BODY>
</HTML>
Я использую NSXMLParser, и он не подходит до него, находят è объект HTML. Это называет foundCharacters: для "До свидания" и затем это называет resolveExternalEntityName:systemID:: с entityName "egrave". В этом методе я просто возвращаю символ "è" trasformed в NSData, foundCharacters называют, снова добавляя строку "è" к предыдущей "До свидания", и затем синтаксический анализатор повышает ошибку NSXMLParserUndeclaredEntityError.
У меня нет DTD, и я не могу изменить файл HTML, который я анализирую. У Вас есть какие-либо идеи об этой проблеме? Заранее спасибо всем Вам, Rob.
Обновление (12/03/2010). После предложения Griffo я закончил с чем-то вроде этого:
data = [self replaceHtmlEntities:data];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser parse];
где replaceHtmlEntities: (NSData *), что-то вроде этого:
- (NSData *)replaceHtmlEntities:(NSData *)data {
NSString *htmlCode = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
NSMutableString *temp = [NSMutableString stringWithString:htmlCode];
[temp replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
[temp replaceOccurrencesOfString:@" " withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
...
[temp replaceOccurrencesOfString:@"À" withString:@"À" options:NSLiteralSearch range:NSMakeRange(0, [temp length])];
NSData *finalData = [temp dataUsingEncoding:NSISOLatin1StringEncoding];
return finalData;
}
Но я все еще смотрю лучший способ решить эту проблему. Я попробую TouchXml в следующие дни, но я все еще думаю, что должен быть способ сделать это использование NSXMLParser API, поэтому если Вы знаете, как, не стесняйтесь писать это здесь :)
После изучения нескольких альтернатив выяснилось, что NSXMLParser не поддерживает сущности, отличные от стандартных сущностей <, >, ', " и &
Приведенный ниже код не работает, что приводит к NSXMLParserUndeclaredEntityError
.
// Create a dictionary to hold the entities and NSString equivalents
// A complete list of entities and unicode values is described in the HTML DTD
// which is available for download http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent
NSDictionary *entityMap = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:@"%C", 0x00E8], @"egrave",
[NSString stringWithFormat:@"%C", 0x00E0], @"agrave",
...
,nil];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:self];
[parser setShouldResolveExternalEntities:YES];
[parser parse];
// NSXMLParser delegate method
- (NSData *)parser:(NSXMLParser *)parser resolveExternalEntityName:(NSString *)entityName systemID:(NSString *)systemID {
return [[entityMap objectForKey:entityName] dataUsingEncoding: NSUTF8StringEncoding];
}
Попытки объявить сущности, дополнив HTML-документ объявлениями ENTITY, проходят, однако расширенные сущности не передаются обратно в parser:foundCharacters
и символы è и à отбрасываются.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
[
<!ENTITY agrave "à">
<!ENTITY egrave "è">
]>
В другом эксперименте я создал полностью валидный xml документ с внутренним DTD
<?xml version="1.0" standalone="yes" ?>
<!DOCTYPE author [
<!ELEMENT author (#PCDATA)>
<!ENTITY js "Jo Smith">
]>
<author>< &js; ></author>
Я реализовал метод делегата parser:foundInternalEntityDeclarationWithName:value:;
и ясно, что парсер получает данные о сущностях, однако parser:foundCharacters
вызывается только для предварительно определенных сущностей.
2010-03-20 12:53:59.871 xmlParsing[1012:207] Parser Did Start Document
2010-03-20 12:53:59.873 xmlParsing[1012:207] Parser foundElementDeclarationWithName: author model:
2010-03-20 12:53:59.873 xmlParsing[1012:207] Parser foundInternalEntityDeclarationWithName: js value: Jo Smith
2010-03-20 12:53:59.874 xmlParsing[1012:207] didStartElement: author type: (null)
2010-03-20 12:53:59.875 xmlParsing[1012:207] parser foundCharacters Before:
2010-03-20 12:53:59.875 xmlParsing[1012:207] parser foundCharacters After: <
2010-03-20 12:53:59.876 xmlParsing[1012:207] parser foundCharacters Before: <
2010-03-20 12:53:59.876 xmlParsing[1012:207] parser foundCharacters After: <
2010-03-20 12:53:59.877 xmlParsing[1012:207] parser foundCharacters Before: <
2010-03-20 12:53:59.878 xmlParsing[1012:207] parser foundCharacters After: <
2010-03-20 12:53:59.879 xmlParsing[1012:207] parser foundCharacters Before: <
2010-03-20 12:53:59.879 xmlParsing[1012:207] parser foundCharacters After: < >
2010-03-20 12:53:59.880 xmlParsing[1012:207] didEndElement: author with content: < >
2010-03-20 12:53:59.880 xmlParsing[1012:207] Parser Did End Document
Я нашел ссылку на учебник по использованию SAX интерфейса LibXML. xmlSAXHandler
, который используется NSXMLParser
, позволяет определить обратный вызов getEntity
. После вызова getEntity
расширение сущности передается обратному вызову characters
.
NSXMLParser
здесь не хватает функциональности. Должно происходить так: NSXMLParser
или его делегат
хранит определения сущностей и передает их обратному вызову xmlSAXHandler
getEntity
. Этого явно не происходит. Я напишу сообщение об ошибке.
Тем временем, предыдущий ответ о выполнении замены строки вполне приемлем, если ваши документы маленькие. Посмотрите учебник по SAX, упомянутый выше, а также пример приложения XMLPerformance от Apple, чтобы понять, стоит ли реализовывать парсер libxml
самостоятельно.
Это было весело.
Вы можете выполнить замену строки в данных перед их разбором с помощью NSXMLParser. Насколько я знаю, NSXMLParser работает только с UTF-8.
Я бы попробовал использовать другой парсер, например libxml2 - теоретически я думаю, что он должен уметь обрабатывать плохой HTML.
Я думаю, вы столкнетесь с другой проблемой с этим примером, поскольку это не действующий XML, который ищет NSXMLParser.
Проблема в том, что теги META, LI, HTML и BODY не закрыты, поэтому синтаксический анализатор полностью просматривает остальную часть документа в поисках закрывающего тега.
Единственный известный мне способ обойти это, если у вас нет доступа для изменения HTML, - это отразить его со вставленными закрывающими тегами.