2013-02-22 2 views
0

Как видно из названия, у меня возникает вопрос о разборе XML-тега, который может иметь несколько атрибутов (или вообще ничего), и я ищу предложения о том, как это можно сделать; но во-первых, я думаю, что немного фона в порядке.php SimpleXMLэлемент синтаксический разбор XML-тегов с несколькими «потенциальными» атрибутами

Я работаю на AIML интерпретатор скриптов PHP на основе называется Program O, и я нахожусь в процессе миграции коды из функций замены строки (например, str_replace, preg_replace и т.д.) с использованием РНР встроенных функций SimpleXML , До сих пор почти все функции синтаксического анализа, которые я создал для различных тегов AIML, были полными и работали неплохо, но один тег, в частности, пинает мое сидение, и это тег CONDITION.

В соответствии с AIML tag reference существуют три отдельных «формы» тега: один с атрибутами NAME и (VALUE | CONTAINS | EXISTS), называемый «множественным состоянием», один с атрибутом NAME, называемый «single name list-condition» и окончательная «форма», называемая «условием списка», которая является просто тегом CONDITION, без каких-либо атрибутов. Ссылка на тезис AIML, с которой я связан ранее, имеет примеры для всех трех форм, но с большим количеством слов между ними, поэтому я буду повторять их здесь в контексте с окружающим кодом AIML:

FORM: multi condition теги:

<category> 
    <pattern>I AM BLOND</pattern> 
    <template>You sound very 
    <condition name="gender" value="female"> attractive.</condition> 
    <condition name="gender" value="male"> handsome.</condition> 
    </template> 
</category> 

ФОРМА: список условие тегов:

<category> 
    <pattern>I AM BLOND</pattern> 
    <template>You sound very 
    <condition> 
     <li name="gender" value="female"> attractive.</li> 
     <li name="gender" value="male"> handsome.</li> 
    </condition> 
    </template> 
</category> 

ФОРМА: одно имя списка-теги состояние

<category> 
    <pattern>I AM BLOND</pattern> 
    <template>You sound very 
    <condition name="gender"> 
     <li value="female"> attractive.</li> 
     <li value="male"> handsome.</li> 
    </condition> 
    </template> 
</category> 

В предыдущей версии сценария, что я работаю, только «список-условие» используется форма СОСТОЯНИЯ тега, и в то время как это наиболее распространенная форма используется, он не используется исключительно , поэтому мне нужно уметь приспосабливаться и к другим двум формам. Так что я задал себе вопрос:

Как это можно сделать эффективным образом?

У меня уже есть рабочий код для разбора формы списка условий тега CONDITION, а предварительное тестирование выглядит многообещающим, поскольку оно не вызывает ошибок и, как представляется, создает желаемые ответы (но только для условия списка форма. Остальные 2 формы с ошибками ошибочны по понятным причинам). Функция приведена ниже:

function parse_condition_tag($convoArr, $element, $parentName, $level) 
{ 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Starting function and setting timestamp.', 2); 
    $response = array(); 
    $attrName = $element['name']; 
    if (!empty ($attrName)) 
    { 
    $attrName = ($attrName == '*') ? $convoArr['star'][1] : $attrName; 
    $search = $convoArr['client_properties'][$attrName]; 
    $path = ($search != 'undefined') ? "//li[@value=\"$search\"]" : '//li[[email protected]*]'; 
    $choice = $element->xpath($path); 
    $children = $choice[0]->children(); 
    if (!empty ($children)) 
    { 
     $response = parseTemplateRecursive($convoArr, $children, $level + 1); 
    } 
    else 
    { 
     $response[] = (string) $choice[0]; 
    } 
    $response_string = implode_recursive(' ', $response, __FILE__, __FUNCTION__, __LINE__); 
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Returning '$response_string' and exiting function.", 4); 
    return $response_string; 
    } 
    trigger_error('Parsing of the CONDITION tag failed! XML = ' . $element->asXML()); 
} 

Я все еще относительно новое для использования функций SimpleXML, так что я вполне может быть что-то очевидное отсутствует. На самом деле, я надеюсь, это точно так. :)

EDIT: Добавление функции, что я, наконец, закончилась с, как и обещал в одном из моих комментариев ниже:

/* 
    * function parse_condition_tag 
    * Acts as a de-facto if/else structure, selecting a specific output, based on certain criteria 
    * @param [array] $convoArr - The conversation array (a container for a number of necessary variables) 
    * @param [object] $element - The current XML element being parsed 
    * @param [string] $parentName - The parent tag (if applicable) 
    * @param [int] $level   - The current recursion level 
    * @return [string] $response_string 
    */ 

function parse_condition_tag($convoArr, $element, $parentName, $level) 
{ 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Starting function and setting timestamp.', 2); 
    global $error_response; 
    $response = array(); 
    $attrName = $element['name']; 
    $attributes = (array)$element->attributes(); 
    $attributesArray = (isset($attributes['@attributes'])) ? $attributes['@attributes'] : array(); 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Element attributes:' . print_r($attributesArray, true), 1); 
    $attribute_count = count($attributesArray); 
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Element attribute count = $attribute_count", 1); 
    if ($attribute_count == 0) // Bare condition tag 
    { 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Parsing a CONDITION tag with no attributes. XML = ' . $element->asXML(), 2); 
    $liNamePath = 'li[@name]'; 
    $condition_xPath = ''; 
    $exclude = array(); 
    $choices = $element->xpath($liNamePath); 
    foreach ($choices as $choice) 
    { 
     $choice_name = (string)$choice['name']; 
     if (in_array($choice_name, $exclude)) continue; 
     $exclude[] = $choice_name; 
     runDebug(__FILE__, __FUNCTION__, __LINE__, 'Client properties = ' . print_r($convoArr['client_properties'], true), 2); 
     $choice_value = get_client_property($convoArr, $choice_name); 
     $condition_xPath .= "li[@name=\"$choice_name\"][@value=\"$choice_value\"]|"; 
    } 
    $condition_xPath .= 'li[not(@*)]'; 
    runDebug(__FILE__, __FUNCTION__, __LINE__, "xpath search = $condition_xPath", 4); 
    $pick_search = $element->xpath($condition_xPath); 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Pick array = ' . print_r($pick_search, true), 2); 
    $pick_count = count($pick_search); 
    runDebug(__FILE__, __FUNCTION__, __LINE__, "Pick count = $pick_count.", 2); 
    $pick = $pick_search[0]; 
    } 
    elseif (array_key_exists('value', $attributesArray) or array_key_exists('contains', $attributesArray) or array_key_exists('exists', $attributesArray)) // condition tag with either VALUE, CONTAINS or EXISTS attributes 
    { 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Parsing a CONDITION tag with 2 attributes.', 2); 
    $condition_name = (string)$element['name']; 
    $test_value = get_client_property($convoArr, $condition_name); 
    switch (true) 
    { 
     case (isset($element['value'])): 
     $condition_value = (string)$element['value']; 
     break; 
     case (isset($element['value'])): 
     $condition_value = (string)$element['value']; 
     break; 
     case (isset($element['value'])): 
     $condition_value = (string)$element['value']; 
     break; 
     default: 
     runDebug(__FILE__, __FUNCTION__, __LINE__, 'Something went wrong with parsing the CONDITION tag. Returning the error response.', 1); 
     return $error_response; 
    } 
    $pick = ($condition_value == $test_value) ? $element : ''; 
    } 
    elseif (array_key_exists('name', $attributesArray)) // this ~SHOULD~ just trigger if the NAME value is present, and ~NOT~ NAME and (VALUE|CONTAINS|EXISTS) 
    { 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Parsing a CONDITION tag with only the NAME attribute.', 2); 
    $condition_name = (string)$element['name']; 
    $test_value = get_client_property($convoArr, $condition_name); 
    $path = "li[@value=\"$test_value\"]|li[not(@*)]"; 
    runDebug(__FILE__, __FUNCTION__, __LINE__, "search string = $path", 4); 
    $choice = $element->xpath($path); 
    $pick = $choice[0]; 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'Found a match. Pick = ' . print_r($choice, true), 4); 
    } 
    else // nothing matches 
    { 
    runDebug(__FILE__, __FUNCTION__, __LINE__, 'No matches found. Returning default error response.', 1); 
    return $error_response; 
    } 
    $children = (is_object($pick)) ? $pick->children() : null; 
    if (!empty ($children)) 
    { 
    $response = parseTemplateRecursive($convoArr, $children, $level + 1); 
    } 
    else 
    { 
    $response[] = (string) $pick; 
    } 
    $response_string = implode_recursive(' ', $response); 
    return $response_string; 
} 

Я подозреваю, что вполне может быть лучше, более элегантный способ (рассказ о моей жизни, действительно), но вышеупомянутое работает по назначению. Любые предложения по улучшению будут с благодарностью приняты и тщательно рассмотрены.

+0

Пожалуйста, обратите внимание здесь, что я не искал кого-то, чтобы «сделать свою домашнюю работу для меня», а, скорее, (не так) нежно подтолкнуть в правильном направлении. Тем не менее, примеры кода по-прежнему приветствуются, но не требуются. :) –

ответ

0

Отметьте, что я не использовал SimpleXML, потому что imho DOMDocument - это просто намного приятнее и waaay более мощным. Оба варианта: DOMDocument и DOMXPath доступны с PHP5.

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

class AIMLParser 
{ 
    public function parse($data) 
    { 
     $internalErrors = libxml_use_internal_errors(true); 

     $dom = new DOMDocument(); 
     $dom->loadHTML($data); 
     $xpath = new DOMXPath($dom); 

     $templates = array(); 

     foreach($xpath->query('//template') as $templateNode) { 
      $template = array(
       'text' => $templateNode->firstChild->nodeValue, // note this expects the first child note to always be the textnode 
       'conditions' => array(), 
      ); 

      foreach ($templateNode->getElementsByTagName('condition') as $condition) { 
       if ($condition->hasAttribute('name') && $condition->hasAttribute('value')) { 
        $template['conditions'] = $this->parseConditionsWithoutChildren($template['conditions'], $condition); 
       } elseif ($condition->hasAttribute('name')) { 
        $template['conditions'] = $this->parseConditionsWithNameAttribute($template['conditions'], $condition); 
       } else { 
        $template['conditions'] = $this->parseConditionsWithoutAttributes($template['conditions'], $condition); 
       } 
      } 

      $templates[] = $template; 
     } 

     libxml_use_internal_errors($internalErrors); 

     return $templates; 
    } 

    private function parseConditionsWithoutChildren(array $conditions, DOMNode $condition) 
    { 
     if (!array_key_exists($condition->getAttribute('name'), $conditions)) { 
      $conditions[$condition->getAttribute('name')] = array(); 
     } 

     $conditions[$condition->getAttribute('name')][$condition->getAttribute('value')] = $condition->nodeValue; 

     return $conditions; 
    } 

    private function parseConditionsWithNameAttribute(array $conditions, DOMNode $condition) 
    { 
     if (!array_key_exists($condition->getAttribute('name'), $conditions)) { 
      $conditions[$condition->getAttribute('name')] = array(); 
     } 

     foreach ($condition->getElementsByTagName('li') as $listItem) { 
      $conditions[$condition->getAttribute('name')][$listItem->getAttribute('value')] = $listItem->nodeValue; 
     } 

     return $conditions; 
    } 

    private function parseConditionsWithoutAttributes(array $conditions, DOMNode $condition) 
    { 
     foreach ($condition->getElementsByTagName('li') as $listItem) { 
      if (!array_key_exists($listItem->getAttribute('name'), $conditions)) { 
       $conditions[$listItem->getAttribute('name')] = array(); 
      } 

      $conditions[$listItem->getAttribute('name')][$listItem->getAttribute('value')] = $listItem->nodeValue; 
     } 

     return $conditions; 
    } 
} 

Что она делает то, что он ищет документ для template узлов и петель через них. Для каждого узла template он узнает, в каком стиле находятся условия. Исходя из этого, он выбирает правильную функцию синтаксиса условий. После прокрутки всех шаблонов он возвращает анализируемый массив со всей информацией (я думаю), в которой вы нуждаетесь.

Чтобы разобрать какой-то документ, который вы можете просто сделать:

$parser = new AIMLParser(); 
$templates = $parser->parse($someVariableWithTheContentOfTheDocument); 

Демо: http://codepad.viper-7.com/JPuBaE

+0

ORLY? Downvote? – PeeHaa

+0

Хотя эта функция не соответствует остальной части скрипта, она ** дает мне направление для исследования, поэтому +1 для этого. Если это так, действительно, приведет к тому, что мне нужно, я обязательно отметю его как «ответ». Спасибо. :) –

+1

Пока я не использовал какой-либо из приведенных выше примеров кода, он дал мне достаточно «подталкивания в правильном направлении», чтобы квалифицировать его как ответ. Я все еще конкретизирую код, но когда я его довожу, я отправлю его сюда, чтобы другие могли извлечь выгоду. Опять же, спасибо, @PeeHaa. –

Смежные вопросы