Парсинг 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 объект.

Рисунок 1  структура CFXMLTree
The structure of a CFXMLTree

Пример в Перечислении 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”.

  Вывод Listing 10 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