0

是否可以在 PHP 中将格式化字符串转换为 xml 格式或数组?我有这样的字符串格式:

$prop = "
    large {
       width: 10px;
       height: 10px;
    }
    small {
       width: 5px;
       height: 5px;
    }
";

我希望将此字符串转换为 xml 格式或数组,例如:

<large>
    <width>10px</width>
    <height>10px</height>
</large>
<small>
    <width>5px</width>
    <height>5px</height>
</small>

$array = array(
    "large" => array(
        "width" => "10px",
        "height" => "10px"
    ),
    "small" => array(
        "width" => "5px",
        "height" => "5px"
    )
);

谢谢。

4

4 回答 4

4

因为您的输入数据格式很好,您可以创建一个非常简单的递归后代解析器 - 甚至不涉及那么多递归。或者只是一个简单的堆栈:

$props = array_filter(array_map('trim', explode("\n", $prop)));
$stack = [$node = $xml = new SimpleXMLElement('<root/>')];

foreach ($props as $str)
{
    if ($str === '}') {
        array_pop($stack);
        $node = end($stack);
        continue;
    }

    if (preg_match('~^(\w+) {$~', $str, $matches)) {
        $node = $stack[] = $node->addChild($matches[1]);
        continue;
    }

    if (preg_match('~^(\w+):\s*(.*)$~', $str, $matches)) {
        $node->addChild($matches[1], htmlspecialchars($matches[2]));
        continue;
    }

    throw new UnexpectedValueException(sprintf('Unable to parse: "%s"', $str));
}

$xml->asXML('php://output');

您的第二个示例(以前缺少)的输出是(美化的):

<?xml version="1.0"?>
<root>
  <button>
    <large>
      <bond>
        <width>10px;</width>
        <height>10px;</height>
      </bond>
      <style>
        <background>
          <color>#ffffff;</color>
          <image>url(www.px.com/aui.png) -10px;</image>
          <another>
            <width>100px;</width>
            <height>100px;</height>
          </another>
        </background>
      </style>
    </large>
    <small>
      <bond>
        <width>10px;</width>
        <height>10px;</height>
      </bond>
      <style>
        <color>#fff;</color>
        <border>1px solid #000;</border>
      </style>
    </small>
  </button>
</root>

我建议您在这里使用 XML,因为它可以比不能有重复键的数组更好地表示结构。

也可以使用递归函数调用而不是堆栈。但这需要将输入流包装在不倒带迭代器中以不中断(或使用array_shift但我不太喜欢):

$parse = function($p, SimpleXMLElement $t) use (&$parse) {
    foreach($p as $s) {
        if ($s === '}') {
            break;
        }

        if (preg_match('~^([^ ]+) {$~', $s, $m)) {
            $p->next();
            $parse($p, $t->addChild($m[1]));
            continue;
        }

        if (preg_match('~^([^:]+):\s*(.*)$~', $s, $m)) {
            $n = $t->addChild($m[1], htmlspecialchars($m[2]));
            continue;
        }
    }
};

$props = array_filter(array_map('trim', explode("\n", $prop)));
$xml = new SimpleXMLElement('<root/>');
$parse(new NoRewindIterator(new ArrayIterator($props)), $xml);

$xml->asXML('php://output');
于 2013-04-07T12:09:36.200 回答
1

有效的解决方案:-)

$prop = "
    large {
       width: 10px;
       height: 10px;
       color: red;
    }
    small {
       width: 5px;
       height: 5px;
    }
    medium {
       width: 5px;
       height: 5px;
    }
";

//remove carriage return
 $prop = str_replace(array('.', ' ', "\n", "\t", "\r"), '', $prop);

 //get into main names and values
 preg_match_all('/([^\{]+)\{([^\}]+)\}/i',$prop,$matches);

 $arr_names = $matches[1];
 $arr_values = $matches[2];

 $arr_result = array();
 foreach($arr_names as $i=>$name){
     $value = $arr_values[$i];

     //get into main sub names and values
     preg_match_all('/([^\:]+)\:([^\;]+)\;/i',$value,$m);
     $arr_n = $m[1];
     $arr_v = $m[2];

     $arr = array();
     foreach($arr_n as $j=>$n){
        $v = $arr_v[$j];   
        $arr[$n] = $v;
     }

     $arr_result[$name] = $arr;
 }

 print_r($arr_result);
于 2013-04-07T10:02:46.197 回答
0

这应该工作

$elements = explode('}', trim($prop));
for($i = 0 ; $i < sizeof($elements); $i++) {
    list($name, $styles) = explode('{', $elements[$i]);
    $name = trim($name);
    if(empty($name)) {
        continue;
    }
    $rules = explode(';', $styles);
    for($j=0; $j < sizeof($rules); $j++) {
        list($ruleName, $ruleVal) = explode(':', $rules[$j]);
        $ruleName = trim($ruleName);
        if(empty($ruleName)) {
            continue;
        }
        $result[$name][$ruleName] = trim($ruleVal);
    }
}   
echo '<pre>';
var_dump($result);
echo '</pre>';
于 2013-04-07T09:33:17.077 回答
-1

我的解决方案:

我尝试使用preg_replace()eval()使用多维格式。也许你可以改进我的preg_replace模式。

$string = "
    button {
        large {
            bond {
                width: 10px;
                height: 10px;
            }
            style {
                background {
                    color: #ffffff;
                    image: url(www.px.com/aui.png) -10px;
                    another {
                        width: 100px;
                        height: 100px;
                    }
                }
            }
        }
        small {
            bond {
                width: 10px;
                height: 10px;
            }
            style {
                color: #fff;
                border: 1px solid #000;
            }
        }
    }
";

$string = preg_replace("/(\s+\:)/", ':', $string);
$string = preg_replace("/(\:\s+)/", ':', $string);
$string = preg_replace("/(\s+\{)/", '{', $string);

$string = preg_replace("/([\w\d_]+)\:(.*?)\;/", '"$1" => "$2",', $string);
$string = preg_replace("/([\w\d_]+)\{/", '"$1" => array (', $string);
$string = preg_replace("/\}/", ')', $string);

$string = preg_replace("/\"\,(\s+)\)/", '"$1)', $string);
$string = preg_replace("/\)(\s+)\"/", '),$1"', $string);

$arr = eval('return array('.$string.');');
print_r($arr);

谢谢。

于 2013-04-07T12:54:10.873 回答