向 HTML 标签添加属性

Add an attribute to an HTML tag

如何转换 html 输入字符串,它来自于:

String tag = "<input type=\"submit\" class=\"cssSubmit\"/>";

"<input type=\"submit\" class=\"cssSubmit disable\" disabled=\"disabled\"/>"

有没有可能的Java或Groovy方法来做到这一点?

例如:

String convert(String input) {
 //input: <input type=\"submit\" class=\"cssSubmit\"/>
 //process the input string
 //processedString: <input type=\"submit\" class=\"cssSubmit disable\" disabled=\"disabled\"/>
 return processedString;
}

这是我能想到的最通用的方式:

public static String editTagXML(String tag,
        Map<String, String> newAttributes,
        Collection<String> removeAttributes)
        throws SAXException, IOException,
        ParserConfigurationException, TransformerConfigurationException,
        TransformerException {
    Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
            .parse(new InputSource(new StringReader(tag)));
    Element root = doc.getDocumentElement();
    NamedNodeMap attrs = root.getAttributes();
    for (String removeAttr : removeAttributes) {
        attrs.removeNamedItem(removeAttr);
    }
    for (Map.Entry<String, String> addAttr : newAttributes.entrySet()) {
        final Attr attr = doc.createAttribute(addAttr.getKey());
        attr.setValue(addAttr.getValue());
        attrs.setNamedItem(attr);
    }
    StringWriter result = new StringWriter();
    final Transformer transformer = TransformerFactory.newInstance()
            .newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.transform(new DOMSource(doc), new StreamResult(result));
    return result.toString();
}

public static void main(String[] args) throws Exception {
    long start = System.nanoTime();
    String tag = "<input type=\"submit\" class=\"cssSubmit\"/>";
    String edited = editTagXML(tag, new HashMap<String, String>() {{
        put("class", "cssSubmit disable");
        put("disabled", "disabled");
    }}, new ArrayList<>());
    long time = System.nanoTime() - start;
    System.out.println(edited);
    System.out.println("Time: " + time + " ns");
    start = System.nanoTime();
    tag = "<input type=\"submit\" class=\"cssSubmit\"/>";
    editTagXML(tag, new HashMap<String, String>() {{
        put("class", "cssSubmit disable");
        put("disabled", "disabled");
    }}, new ArrayList<>());
    time = System.nanoTime() - start;
    System.out.println("Time2: " + time + " ns");
}

它丑陋、庞大、复杂,抛出大量已检查的异常并混淆了可能重要也可能不重要的属性顺序。这可能不是应该如何完成的。它也很慢。

这是输出:

<input class="cssSubmit disable" disabled="disabled" type="submit"/>
Time: 86213231 ns
Time2: 2379674 ns

第一个 运行 可能很慢,因为加载必要的库需要一段时间。第二个 运行 出奇的快,但我的电脑也很强大。如果你对你的输入施加一些限制(比如,属性值只用 " 引号,属性值中没有 " 等等),可能会有更好的方法来做到这一点,比如使用正则表达式或者甚至简单的迭代。

例如,如果您的输入总是这样,那么这也可以:

    start = System.nanoTime();
    edited = tag.replaceFirst("\"cssSubmit\"", "\"cssSubmit disable\" disabled=\"disabled\"");
    time = System.nanoTime() - start;
    System.out.println(edited);
    System.out.println("Time3: " + time + " ns");

输出:

<input type="submit" class="cssSubmit disable" disabled="disabled"/>
Time3: 1422672 ns

嗯。有趣的是,它并没有那么快。

好的,但是如果我们想要一个更通用但仍然足够简单的解决方案怎么办?我们可以使用正则表达式:

private static final Pattern classAttributePattern
        = Pattern.compile("\bclass=\"([^\"]+)\"");
public static String disableTag(String tag) {
    Matcher matcher = classAttributePattern.matcher(tag);
    if (!matcher.find()) {
        throw new IllegalArgumentException("Doesn't match: " + tag);
    }
    int start = matcher.start();
    int end = matcher.end();
    String classValue = matcher.group(1);
    if (classValue.endsWith(" disable")) {
        return tag; // already disabled
    } else {
        // assume that if the class doesn't end with " disable",
        // then the disabled attribute is not present as well
        return tag.substring(0, start)
                + "class=\"" + classValue
                + " disable\" disabled=\"disabled\""
                + tag.substring(end);
    }
}

注意通常使用正则表达式XML/(X)HTML是极端错误-易于。以下是可能破坏上述代码的示例输入的非详尽列表:

  • <input type="submit" class="cssSubmit disable " disabled="disabled"/> - 这会因为引号前的 space 而中断;
  • <input type="submit" class='cssSubmit disable' disabled="disabled"/> - 这会中断,因为我们的代码不需要单引号;
  • <input type="submit" class = "cssSubmit" disabled="disabled"/> - 这会中断,因为 = 周围有 space;
  • <input title='this is an input with class="cssSubmit" that could be changed to class="cssSubmit disable"' type="submit" class="cssSubmit" disabled="disabled"/> - 这会中断,因为另一个属性的值中有类似属性的文本。

每种情况都可以通过以某种方式修改模式来解决(尽管我不确定最后一种情况),但是当它中断时您可以找到另一种情况。所以这种技术最适用于由程序生成的输入,而不是人工编写的输入,即使这样你也应该注意该程序的输入来自何处(它很容易包含属性值,就像上一个例如)。

您可以在 groovy 中执行此操作:

String tag = "<input type=\"submit\" class=\"cssSubmit\"/>"

tag = new XmlSlurper().parseText(tag).with { x ->
    x.@class = 'cssSubmit disable'
    x.@disabled = 'disabled'
    new groovy.xml.StreamingMarkupBuilder().bind { delegate.out << x}.toString()
}​