XML标签名称中的字符无效

时间:2020-01-31 09:21:26

标签: php xml dom

我们有一个庞大的数据库,用户可以在其中创建自定义字段。名称中允许使用每个UTF-8字符。直到几周前,当他们以XML格式导出数据时,只有用户在表中具有的无效字符才用斜杠/和空格斜杠,我们用下划线代替了它们。

现在,我看到一些需要XML导出的用户在其字段名*!中使用...因此,例如,以他们的字段名valid_name命名invalid*name!,此脚本将中断。

用于定义标签名称的部分代码:

$doc = new DOMDocument();

$elementName = is_numeric($key) ? (string)$name : (string)$key;
$elementName = str_replace(array('/', ' '), '_', trim($elementName));

$node = $doc->createElement($elementName); // here I get error "invalid character name"

有效XML的示例:

<?xml version="1.0"?>
<rows total="621" page="1">
    <row>
        <valid_name>60E49542D19D16EDB633A40</valid_name>
    ....

我不需要用户在其元素名称!*中看到...我需要知道元素名称中不允许包含哪些字符,可能会用下划线替换它们,如果您有更好的主张而不是用下划线替换它们,我也将开放。

2 个答案:

答案 0 :(得分:1)

@Quentin建议使用更好的方法。使用动态节点名称意味着您无法定义XSD / Schema,XML文件只会格式正确。您将无法充分利用验证程序。因此,从机器可读性和维护的角度来看,<field name="..."/>是更好的解决方案。

但是,NCName(非冒号名称)允许使用很多字符。这是我在库中实现的用于转换JSON的内容。

$nameStartChar定义字母和几个Unicode范围。 $nameChar在该定义中添加了更多字符(例如数字)。

第一个RegExp删除任何不是名称char的字符。第二个命令删除所有未在$nameStartChar中定义的起始字符。如果结果为空,则将返回默认名称。

function normalizeString(string $string, string $default = '_'): string {
    $nameStartChar =
      'A-Z_a-z'.
      '\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}'.
      '\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}'.
      '\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}'.
      '\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}';
    $nameChar =
      $nameStartChar.
      '\\.\\d\\x{B7}\\x{300}-\\x{36F}\\x{203F}-\\x{2040}';
    $result = \preg_replace(
      [
        '([^'.$nameChar.'-]+)u',
        '(^[^'.$nameStartChar.']+)u',
      ],
      '',
      $string
    );
    return empty($result) ? $default : $result;
} 

合格的XML节点名称可以包含两个以':'分隔的NC名称。第一部分是名称空间前缀。

$examples = [
  '123foo', 
  'foo123', 
  '  foo  ', 
  '  ', 
  'foo:bar', 
  'foo-bar'
];

foreach ($examples as $example) {
    var_dump(normalizeString($example));
}

输出:

string(3) "foo"
string(6) "foo123"
string(3) "foo"
string(1) "_"
string(6) "foobar"
string(7) "foo-bar"

答案 1 :(得分:-1)

我将使用rawurlencode来解码名称。作为字段名称,输出应该可以,并且您可以根据需要进行编码。用下划线代替将无法实现相反的操作。


编辑: 我错了,这并不容易。这两个功能应该可以解决问题:

<ItemsControl ItemsSource="{Binding Questions}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*"/>
                    <ColumnDefinition Width="8*"/>
                </Grid.ColumnDefinitions>
                <Label Grid.Column="0" Content="{Binding RequiredText}" />
                <TextBox x:Name="textBox" Grid.Column="1" Text="{Binding InputText}">
                    <TextBox.Style>
                        <Style TargetType="TextBox">
                            <Setter Property="BorderBrush" Value="Red" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsCorrect}"  Value="true">
                                    <Setter Property="BorderBrush" Value="Green" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
  • 仅保留名称中的ascii字符
  • 避免以下划线或数字开头。