Самый стабильный и безопасный способ сделать это - загрузить HTML в DOM, убрать из него ненужные биты (или, что более безопасно, удалить все, кроме нужных бит) и преобразовать результат. вернуться к строке.
Однако - насколько мне известно, ColdFusion не предоставляет собственный анализатор DOM для HTML (только один для XML), а редактор форматированного текста YUI не создает XML (то есть XHTML). Это немного прискорбно, но не обязательно тупик.
- Для Java доступно множество HTML-анализаторов, и использовать объекты Java из ColdFusion легко. Вы можете включить один из них в свой проект.
- Вы можете преобразовать ввод HTML в XHTML (через jTidy), а затем использовать встроенный анализатор XML для реализации очистки. Вы можете даже преобразовать его обратно в HTML с помощью jTidy после того, как закончите.
Для начала я создал пример строгого белого списка для HTML-элементов и атрибутов во встроенном синтаксическом анализаторе XML:
<!--- to serve as an example of what you would get from jTidy --->
<cfset xhtml = XmlParse('
<html xmlns="http://www.w3.org/1999/xhtml">
foo <img src="foo.jpg" title="Foo!" alt="Foo!" height="100" width="100" style="border=1;" />
bar <a href="asdasdad" title="blah" target="baz" onmouseover="doSomethingEvil();">Link</a>
baz <script type="text/javascript">doSomethingEvil();</script>
</html>', true)>
<!--- an easily configurable list of allowed elements and attributes --->
<cfset whiteList = StructNew()>
<cfset whiteList["html"] = "xmlns">
<cfset whiteList["head"] = "">
<cfset whiteList["body"] = "">
<cfset whiteList["img"] = "src">
<cfset whiteList["a"] = "href,title,name">
<!--- delete all attributes that are not white-listed --->
<cfloop collection="#whiteList#" item="tag">
<cfset nodes = XmlSearch(xhtml, "//*[local-name() = '#tag#']")>
<cfloop from="1" to="#ArrayLen(nodes)#" index="i">
<cfset nodeAttrs = nodes[i].XmlAttributes>
<cfloop list="#StructKeyList(nodeAttrs)#" index="attr">
<cfif not ListFind(whiteList[tag], attr)>
<cfset StructDelete(nodeAttrs, attr)>
</cfif>
</cfloop>
</cfloop>
</cfloop>
<!--- delete all elements that are not white-listed --->
<cfset unwantedElements = XmlSearch(xhtml, "//*[not(contains(',#StructKeyList(whiteList)#,', concat(',',local-name(),',')))]")>
<cfloop from="1" to="#ArrayLen(unwantedElements)#" index="i">
<cfset node = unwantedElements[i]>
<cfset node.XmlAttributes["x-delete-flag"] = "true">
<cfset parent = XmlSearch(node, "..")>
<cfif ArrayLen(parent) eq 1 and StructKeyExists(parent[1], "XmlChildren")>
<cfset childNodes = parent[1].XmlChildren>
<cfloop from="#ArrayLen(childNodes)#" to="1" step="-1" index="k">
<cfif StructKeyExists(childNodes[k].XmlAttributes, "x-delete-flag")>
<cfset ArrayDeleteAt(childNodes, k)>
</cfif>
</cfloop>
</cfif>
</cfloop>
Когда все готово, содержимое xhtml
выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
foo <img src="foo.jpg"/>
bar <a href="asdasdad" title="blah">Link</a>
baz
</html>
Несколько объяснений:
- Существует несколько описаний и UDF о том, как заставить Tidy работать в ColdFusion в Интернете, просто посмотрите на них.
- Обработка XML DOM является громоздкой в ColdFusion. Это не красиво и не элегантно, но все же лучше, чем пытаться использовать (не дай Бог) регулярные выражения для достижения того же эффекта. Я настоятельно не рекомендую вам использовать их для этой проблемы.
- Используйте
<cfdump>
, чтобы почувствовать, как ColdFusion представляет XML-документы, и понять, что происходит в моем коде.
- Второй бит (удаление всех элементов, не занесенных в белый список) немного волосатый. Очевидно, что невозможно более элегантно удалить узел из ColdFusion, поскольку XML-узлы ColdFusion не предоставляют ни методов
parentNode()
, ни removeChild()
DOM. Эта реализация основана на подходе Бена Наделя к удалению узлов DOM в CF . Это работает, но я до боли осознаю, что это отстой. Простите за это. : - \
- XPath: выражение
"//*[not(contains(',#StructKeyList(whiteList)#,', concat(',',local-name(),',')))]"
выбирает все узлы, локальное имя которых (т.е. не глядя на пространство имен XML) не содержится в списке разрешенных имен. В деталях:
//
- сокращение от «где угодно в документе».
*
означает «узел любого элемента».
- Квадратные скобки обозначают условие. Дополнительные запятые должны гарантировать, что учитываются только полные совпадения - в противном случае
contains()
вернет "a"
в качестве совпадения, например, в "abbr"
.