向 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()
}
如何转换 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()
}