Java - 如何检查字符串是否为有效的XML元素名称?

12

你知道Java中一个可以验证一个字符串是否为合法XML元素名称的函数吗?

来自w3schools的表单:

XML元素必须遵循以下命名规则:

  1. 名称可以包含字母,数字和其他字符
  2. 名称不能以数字或标点符号字符开头
  3. 名称不能以字母xml(或XML或Xml等)开头
  4. 名称不能包含空格

我发现其他问题提供了正则表达式解决方案,难道没有已经可以执行该操作的函数吗?

4个回答

13

如果你正在使用Xerces XML解析器,你可以使用XMLChar(或XML11Char)类的isValidName()方法,像这样:

org.apache.xerces.util.XMLChar.isValidName(String name)

这里还有一个示例代码,用于验证isValidName函数。


1
很好,它看起来正是我想要的,但你知道为什么XMLChar.isValidName("xml")返回true吗?(问题已批准) - ekeren
1
不区分大小写地测量,“xml”是有效的 - 但是保留的。在实践中,您可能会遇到它。如果您正在检查输入,则可能希望添加&&!name.toLowerCase()。startsWith(“xml”) - lavinio
检查名称有效性所需的代码实际上只包含几行,可以复制出来避免另一个外部依赖。有关详细信息,请参见我的答案 - pemistahl

4

相关规范的生产是http://www.w3.org/TR/xml/#NT-Name

Name ::== NameStartChar NameChar *

NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]

NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]

因此,匹配它的正则表达式为

"^[:A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02ff\\u0370-\\u037d"
+ "\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\ud7ff"
+ "\\uf900-\\ufdcf\\ufdf0-\\ufffd\\x10000-\\xEFFFF]"
+ "[:A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6"
+ "\\u00F8-\\u02ff\\u0370-\\u037d\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f"
+ "\\u2c00-\\u2fef\\u3001-\\udfff\\uf900-\\ufdcf\\ufdf0-\\ufffd\\-\\.0-9"
+ "\\u00b7\\u0300-\\u036f\\u203f-\\u2040]*\\Z"

如果你想处理命名空间名称,需要确保最多只有一个冒号,因此

"^[A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02ff\\u0370-\\u037d"
+ "\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\udfff"
+ "\\uf900-\\ufdcf\\ufdf0-\\ufffd]"
+ "[A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02ff\\u0370-\\u037d"
+ "\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\udfff"
+ "\\uf900-\\ufdcf\\ufdf0-\\ufffd\\-\\.0-9\\u00b7\\u0300-\\u036f\\u203f-\\u2040]*"
+ "(?::[A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02ff\\u0370-\\u037d"
+ "\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\udfff"
+ "\\uf900-\\ufdcf\\ufdf0-\\ufffd]"
+ "[A-Z_a-z\\u00C0\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02ff\\u0370-\\u037d"
+ "\\u037f-\\u1fff\\u200c\\u200d\\u2070-\\u218f\\u2c00-\\u2fef\\u3001-\\udfff"
+ "\\uf900-\\ufdcf\\ufdf0-\\ufffd\\-\\.0-9\\u00b7\\u0300-\\u036f\\u203f-\\u2040]*)?\\Z"

错过了另一个03gf;将两个都更改为036f。


谢谢,这是否意味着规则3不正确:“3. 名称不能以字母xml(或XML、Xml等)开头”? - ekeren
答案是肯定的和否定的。以字符串“xml”开头的名称,或与匹配(('X'|'x')('M'|'m')('L'|'l'))的任何字符串开头的名称,都保留用于在此规范的当前或未来版本中进行标准化。因此,它是一个有效的名称,但是被保留了。 - Mike Samuel
根据我对原始制作的理解,这些正则表达式包含多个错误:除了第一个字符类以外的所有字符类中都使用了\\udfff而不是\\ud7ff,且范围\\x10000-\\xEFFFF缺失。在Java中,此范围也需要是\x{10000}-\x{EFFFF}(缺少{})。不幸的是,由于有太多待处理的编辑,我无法提出修改建议。 - michitux
我认为在标准库的早期版本中,这些样式转义可能无法正常工作。如果您有更好的方法,请进行编辑。 - Mike Samuel

2
使用org.apache.xerces工具是一个不错的选择;然而,如果您需要使用标准Java API中的Java代码,则可以使用以下代码:
public void parse(String xml) throws Exception {

    XMLReader parser = XMLReaderFactory.createXMLReader();
    parser.setContentHandler(new DefaultHandler());
    InputSource source = new InputSource(new ByteArrayInputStream(xml.getBytes()));
    parser.parse(source);
}

但请注意,为此任务实例化XMLReader的开销相当高,特别是如果使用JAXP工厂搜索完成。当然,如果经常重复使用,则没有问题。 - Michael Kay

2
作为对已接受的答案的最新补充:
至少Oracle的JDK 1.8(可能还包括旧版本)在非公共com.sun.*包中内部使用Xerces解析器。您不应直接使用来自那些类的任何实现,因为它们在将来的JDK版本中可能会无预警地更改!但是,xml元素名称有效性检查所需的代码已经很好地封装,并且可以复制到您自己的代码中。这样,您就可以避免对外部库的另一个依赖。
下面是从内部类com.sun.org.apache.xerces.internal.util.XMLChar中提取的所需代码:
public class XMLChar {

    /** Character flags. */
    private static final byte[] CHARS = new byte[1 << 16];

    /** Name start character mask. */
    public static final int MASK_NAME_START = 0x04;

    /** Name character mask. */
    public static final int MASK_NAME = 0x08;

    static {
        // Initializing the Character Flag Array
        // Code generated by: XMLCharGenerator.

        CHARS[9] = 35;
        CHARS[10] = 19;
        CHARS[13] = 19;

        // ...
        // the entire static block must be copied
    }

    /**
     * Check to see if a string is a valid Name according to [5]
     * in the XML 1.0 Recommendation
     *
     * @param name string to check
     * @return true if name is a valid Name
     */
    public static boolean isValidName(String name) {
        final int length = name.length();
        if (length == 0) {
            return false;
        }
        char ch = name.charAt(0);
        if (!isNameStart(ch)) {
            return false;
        }
        for (int i = 1; i < length; ++i) {
            ch = name.charAt(i);
            if (!isName(ch)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns true if the specified character is a valid name start
     * character as defined by production [5] in the XML 1.0
     * specification.
     *
     * @param c The character to check.
     */
    public static boolean isNameStart(int c) {
        return c < 0x10000 && (CHARS[c] & MASK_NAME_START) != 0;
    }

    /**
     * Returns true if the specified character is a valid name
     * character as defined by production [4] in the XML 1.0
     * specification.
     *
     * @param c The character to check.
     */
    public static boolean isName(int c) {
        return c < 0x10000 && (CHARS[c] & MASK_NAME) != 0;
    }
}

无法使用jdk 11编译:错误: [ERROR](包com.sun.org.apache.xerces.internal.util在模块java.xml中声明,该模块未导出它) - Valentyn Kolesnikov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接