Парсинг XML-документов
Документ, показанный в Перечислении 1, содержит представление XML очень простого Базового списка свойств Основы, создаваемого с помощью CFPropertyList. Обратите внимание на то, что список свойств был выбран просто в целях иллюстрирования использования синтаксического анализатора XML в Базовом контексте Основы. CFPropertyList имеет функции удобства для преобразования списков свойств к и от формата XML, таким образом, в большинстве случаев Ваше приложение не должно было бы анализировать список свойств XML с помощью синтаксического анализатора XML непосредственно (см., что Список свойств Программирует Темы для Базовой Основы для получения дополнительной информации).
Перечисление 1 Базовый список свойств Основы в формате XML
<?xml version="1.0" encoding="UTF-8"?> |
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> |
<plist version="0.9"> |
<dict> |
<key>Jane Doe</key> |
<integer>1999</integer> |
<key>John Doe</key> |
<integer>2000</integer> |
</dict> |
</plist> |
В этом XML-документе в качестве примера данные состоят из двух имен и связанные годы рождения. <plist> тег объявляет, что прилагаемые данные являются списком свойств, соответствующим Базовому типу данных Основы CFPropertyList. <dict> тег объявляет, что его прилагаемые данные соответствуют CFDictionary. Наконец, имя и данные года рождения перечислены в формате пары ключ/значение, требуемом для a CFDictionary объект.
Используя основанный на дереве интерфейс синтаксического анализатора
Перечисление 2 показывает, как Вы использовали бы XML высокого уровня API для преобразования демонстрационных данных XML в Перечислении 1 в a CFXMLTree объект. Этот пример принимает это sourceURL допустимое CFURL возразите и обращается к XML-документу.
Перечисление 2 Используя основанный на дереве синтаксический анализатор API
CFXMLTreeRef cfXMLTree; |
CFDataRef xmlData; |
// Load the XML data using its URL. |
CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, |
sourceURL, &xmlData, NULL, NULL, NULL) |
// Parse the XML and get the CFXMLTree. |
cfXMLTree = CFXMLTreeCreateFromData(kCFAllocatorDefault, |
xmlData, |
sourceURL, |
kCFXMLParserSkipWhitespace, |
kCFXMLNodeCurrentVersion); |
Рисунок 1 иллюстрирует структуру CFXMLTree объект производится кодом в Перечислении 2. Как Вы ожидали бы, это точно отражает структуру исходного XML-документа. Схема выводит на экран код типа данных и строку данных от каждого CFXMLNode объект.

Пример в Перечислении 3 показывает, как использовать некоторые функции удобства XML для исследования верхнего уровня a CFXMLTree возразите и распечатайте строковое содержание данных каждого узла.
Перечисление 3 Получая информацию из CFXMLTree
CFXMLTreeRef xmlTreeNode; |
CFXMLNodeRef xmlNode; |
int childCount; |
int index; |
// Get a count of the top level node’s children. |
childCount = CFTreeGetChildCount(cfXMLTree); |
// Print the data string for each top-level node. |
for (index = 0; index < childCount; index++) { |
xmlTreeNode = CFTreeGetChildAtIndex(cfXMLTree, index); |
xmlNode = CFXMLTreeGetNode(xmlTreeNode); |
CFShow(CFXMLNodeGetString(xmlNode)); |
} |
Используя событийно-управляемый интерфейс синтаксического анализатора
Событийно-управляемый API синтаксического анализатора дает Вам полную гибкость, чтобы сделать независимо от того, что Вы желаете с данными в XML-документе. Для использования событийно-управляемого синтаксического анализатора API Вы определяете ряд функций обратного вызова, которые вызывает синтаксический анализатор, поскольку это встречается с определенными структурами в XML-документе. Код в этом разделе показывает, как использовать событийно-управляемый синтаксический анализатор для печати данных в XML-документе. Демонстрационная реализация для каждой функции обратного вызова показана, и затем код, чтобы создать и выполнить синтаксический анализатор.
Код в Перечислении 4 реализует первое — и безусловно самое длинное — функция обратного вызова, CFXMLParserCreateXMLStructureCallBack. Эта реализация в качестве примера распечатывает содержание данных дополнительной информации каждой новой структуры XML, поскольку с этим встречаются.
Перечисление 4 Реализовывая функцию CFXMLParserCreateXMLStructureCallBack
void *createStructure(CFXMLParserRef parser, |
CFXMLNodeRef node, void *info) { |
CFStringRef myTypeStr; |
CFStringRef myDataStr; |
CFXMLDocumentInfo *docInfoPtr; |
// Use the dataTypeID to determine what to print. |
switch (CFXMLNodeGetTypeCode(node)) { |
case kCFXMLNodeTypeDocument: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeDocument\n"); |
docInfoPtr = CFXMLNodeGetInfoPtr(node); |
myDataStr = CFStringCreateWithFormat(NULL, |
NULL, |
CFSTR("Document URL: %@\n"), |
CFURLGetString(docInfoPtr->sourceURL)); |
break; |
case kCFXMLNodeTypeElement: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeElement\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("Element: %@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeProcessingInstruction: |
myTypeStr = CFSTR("Data Type ID: |
kCFXMLNodeTypeProcessingInstruction\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("PI: %@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeComment: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeComment\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("Comment: %@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeText: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeText\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("Text:%@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeCDATASection: |
myTypeStr = CFSTR("Data Type ID: k |
CFXMLDataTypeCDATASection\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("CDATA: %@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeEntityReference: |
myTypeStr = CFSTR("Data Type ID: |
kCFXMLNodeTypeEntityReference\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("Entity reference: %@\n"), |
CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeDocumentType: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeDocumentType\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("DTD: %@\n"), CFXMLNodeGetString(node)); |
break; |
case kCFXMLNodeTypeWhitespace: |
myTypeStr = CFSTR("Data Type ID: kCFXMLNodeTypeWhitespace\n"); |
myDataStr = CFStringCreateWithFormat(NULL, NULL, |
CFSTR("Whitespace: %@\n"), CFXMLNodeGetString(node)); |
break; |
default: |
myTypeStr = CFSTR("Data Type ID: UNKNOWN\n"); |
myDataStr = CFSTR("Unknown type.\n"); |
} |
// Print the contents. |
printf("---Create Structure Called--- \n"); |
CFShow(myTypeStr); |
CFShow(myDataStr); |
// Return the data string for use by the addChild and |
// endStructure callbacks. |
return myDataStr; |
} |
Заметьте что CFXMLParserCreateXMLStructureCallBack функционируйте возвращается, строка данных создала использование dataString поле структуры, с которой недавно встречаются. Это возвращаемое значение может фактически быть чем-либо, но сохраняется синтаксическим анализатором и пасуется назад Вам обоими CFXMLParserAddChildCallBack и CFXMLParserEndXMLStructureCallBack функции, описанные ниже. Обратите внимание на то, что, если Ваш CFXMLParserCreateXMLStructureCallBack функциональные возвраты NULL, CFXMLParserAddChildCallBack и CFXMLParserEndXMLStructureCallBack не будет вызван. Единственное исключение CFNodeTypeDocument; CFXMLParserEndXMLStructureCallBack даже если Вы возвратитесь, будет вызван для него NULL от CFXMLParserCreateXMLStructureCallBack.
Синтаксический анализатор вызывает CFXMLParserAddChildCallBack когда это встречается с дочерним элементом последний раз проанализированной структуры. В этом примере, CFXMLParserAddChildCallBack обратный вызов, показанный в Перечислении 5 просто, распечатывает обе из строк, чтобы ясно дать понять отношения отцов и детей проанализированных структур XML.
Перечисление 5 Реализовывая функцию CFXMLParserAddChildCallBack
void addChild(CFXMLParserRef parser, void *parent, void *child, void *info) { |
printf("---Add Child Called--- \n"); |
printf("Parent being added to: "); CFShow((CFStringRef)parent); |
printf("Child being added: "); CFShow((CFStringRef)child); |
} |
Синтаксический анализатор вызывает CFXMLParserEndXMLStructureCallBack функция, реализованная в Перечислении 6, когда это перемещается вне данной структуры. xmlType параметр является указателем на любые данные CFXMLParserCreateXMLStructureCallBack когда с открытым тегом структуры сначала встретились, функция возвратилась. В этой реализации в качестве примера обратный вызов распечатывает строку, указывающую, какая структура закончилась.
Перечисление 6 Реализовывая endStructure обратный вызов
void endStructure(CFXMLParserRef parser, void *xmlType, void *info) { |
// Leave evidence that we were called. |
printf("---End Structure Called for \n"); CFShow((CFStringRef)xmlType); |
// Now that the structure and all of its children have been parsed, |
// we can release the string. |
CFRelease(xmlType); |
} |
Синтаксический анализатор вызывает CFXMLParserResolveExternalEntityCallBack функционируйте, когда это встретится с внешней ссылкой на сущность. Данные XML в качестве примера в этом разделе не содержат ссылок на сущность, таким образом, не вызывается этот обратный вызов. Перечисление 7 показывает минимальную реализацию.
Перечисление 7 Реализовывая функцию CFXMLParserResolveExternalEntityCallBack
CFDataRef resolveEntity(CFXMLParserRef parser, CFStringRef publicID, |
CFURLRef systemID, void *info) { |
printf("---resolveEntity Called---\n"); |
return NULL; |
} |
Синтаксический анализатор вызывает CFXMLParserHandleErrorCallBack обратный вызов, когда это встречается с состоянием ошибки. Как показано в Перечислении 8, можно использовать API XML для получения и строки ошибки и ошибочной информации о расположении от синтаксического анализатора. Если Вы возвращаетесь false от этого обратного вызова, аварийных прекращений работы синтаксического анализатора. Если Вы возвращаетесь true и ошибка является нефатальной, синтаксический анализатор продолжает обрабатывать.
Перечисление 8 Реализовывая функцию handleError CFXMLParserHandleErrorCallBack
Boolean handleError(CFXMLParserRef parser, SInt32 error, void *info) { |
char buf[512], *s; |
// Get the error description string from the Parser. |
CFStringRef description = CFXMLParserCopyErrorDescription(parser); |
s = (char *)CFStringGetCStringPtr(description, |
CFStringGetSystemEncoding()); |
// If the string pointer is unavailable, do some extra work. |
if (!s) { |
CFStringGetCString(description, buf, 512, |
CFStringGetSystemEncoding()); |
} |
CFRelease(description); |
// Report the exact location of the error. |
fprintf(stderr, "Parse error (%d) %s on line %d, character %d\n", |
(int)error, |
s, |
(int)CFXMLParserGetLineNumber(parser), |
(int)CFXMLParserGetLocation(parser)); |
return false; |
} |
Перечисление 9 демонстрирует, как создать и вызвать синтаксический анализатор.
Создание перечисления 9 и вызов синтаксического анализатора XML
// First, set up the parser callbacks. |
CFXMLParserCallBacks callbacks = {0, createStructure, addChild, endStructure, resolveEntity, handleError}; |
// Create the parser with the option to skip whitespace. |
parser = CFXMLParserCreate(kCFAllocatorDefault, xmlData, urlOut, kCFXMLParserSkipWhitespace, kCFXMLNodeCurrentVersion, &callbacks, NULL); |
// Invoke the parser. |
if (!CFXMLParserParse(parser)) { |
printf("parse failed\n"); |
} |
Как Вы видите, как только обратные вызовы были реализованы, код, чтобы создать и вызвать синтаксический анализатор довольно прост. “Parser output” показывает вывод, сгенерированный кодом в “Creating and invoking the XML parser”.
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeDocument, Document: file://localhost/myPlist.xml |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeProcessingInstruction, PI: xml |
---Add Child Called--- |
Parent being added to: Document: file://localhost/myPlist.xml |
Child being added: PI: xml |
---End Structure Called for PI: xml |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeDocumentType, DTD |
---Add Child Called--- |
Parent being added to: Document: file://localhost/myPlist.xml |
Child being added: DTD |
---End Structure Called for DTD |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: plist |
---Add Child Called--- |
Parent being added to: Document: file://localhost/myPlist.xml |
Child being added: Element: plist |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: dict |
---Add Child Called--- |
Parent being added to: Element: plist |
Child being added: Element: dict |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: key |
---Add Child Called--- |
Parent being added to: Element: dict |
Child being added: Element: key |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeText, Text: Jane Doe |
---Add Child Called--- |
Parent being added to: Element: key |
Child being added: Text: Jane Doe |
---End Structure Called for Text: Jane Doe |
---End Structure Called for Element: key |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: integer |
---Add Child Called--- |
Parent being added to: Element: dict |
Child being added: Element: integer |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeText, Text: 1999 |
---Add Child Called--- |
Parent being added to: Element: integer |
Child being added: Text: 1999 |
---End Structure Called for Text: 1999 |
---End Structure Called for Element: integer |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: key |
---Add Child Called--- |
Parent being added to: Element: dict |
Child being added: Element: key |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeText, Text: John Doe |
---Add Child Called--- |
Parent being added to: Element: key |
Child being added: Text: John Doe |
---End Structure Called Text: John Doe |
---End Structure Called for Element: key |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeElement, Element: integer |
---Add Child Called--- |
Parent being added to: Element: dict |
Child being added: Element: integer |
---Create Structure Called--- |
Data Type ID: kCFXMLNodeTypeText, Text: 2000 |
---Add Child Called--- |
Parent being added to: Element: integer |
Child being added: Text: 2000 |
---End Structure Called for Text: 2000 |
---End Structure Called for Element: integer |
---End Structure Called for Element: dict |
---End Structure Called for Element: plist |