你怎么能在网页上编辑有效的XML?

时间:2008-10-28 18:18:42

标签: c# xml serialization character-encoding

我必须启动并运行快速而脏的配置编辑器。流程如下:

配置(服务器上的POCO)序列化为XML 此时XML已经很好地形成了。配置将发送到XElements中的Web服务器 在Web服务器上,XML(是的,所有的IT)被转储到textarea中进行编辑 用户直接在网页中编辑XML并单击“提交” 在响应中,我检索XML配置的更改文本。此时,所有转义都已通过在网页中显示它们的过程而恢复 我尝试将字符串加载到XML对象(XmlElement,XElement,等等)。 KABOOM。

问题是序列化会转义属性字符串,但在转换过程中会丢失。

例如,假设我有一个具有正则表达式的对象。以下是Web服务器的配置:

<Configuration>
  <Validator Expression="[^&lt;]" />
</Configuration>

所以,我把它放到textarea中,对用户来说就是这样:

<Configuration>
  <Validator Expression="[^<]" />
</Configuration>

因此,用户稍作修改并提交更改。在Web服务器上,响应字符串如下所示:

<Configuration>
  <Validator Expression="[^<]" />
  <Validator Expression="[^&]" />
</Configuration>

因此,用户添加了另一个验证器,现在BOTH具有非法字符的属性。如果我尝试将其加载到任何XML对象中,它会抛出异常,因为&lt;和&amp;在文本字符串中无效。我不能不能使用任何编码功能,因为它编码整个血腥的东西:

var result = Server.HttpEncode(editedConfig);

结果

&lt;Configuration&gt;
  &lt;Validator Expression="[^&lt;]" /&gt;
  &lt;Validator Expression="[^&amp;]" /&gt;
&lt;/Configuration&gt;

这不是有效的XML。如果我尝试将其加载到任何类型的XML元素中,我将会被一个下降的铁砧击中。我不喜欢掉落的铁砧。

所以问题仍然存在......我是否可以通过使用正则表达式替换来获得解析为XML对象的XML字符串的唯一方法?我加载时有没有办法“关闭约束”?你怎么绕过这个?


最后一个回复,然后维护这个,因为我认为没有一个有效的答案。

我在textarea中放置的XML是有效的,转义的XML。 1)将其放入文本区域的过程2)将其发送给客户端3)将其显示给客户端4)提交表单,然后将其发送回服务器,以及6)从表格中检索值REMOVES任何和所有逃脱。

让我再说一遍:我不会逃避任何事情。只需在浏览器中显示它就可以了!

要考虑的事情:有没有办法阻止这种逃避行为的发生?有没有办法采用几乎有效的XML并以安全的方式“清理”它?


这个问题现在有了它的赏金。要收集赏金,您将演示如何在浏览器窗口中编辑VALID XML而不使用第三方/开源工具,该工具不需要我使用正则表达式手动转义属性值,这不需要用户转义其属性,并且在往返时不会失败(&amp; amp; amp; amp; amp; etc;)

8 个答案:

答案 0 :(得分:7)

呃... 你如何序列化?通常,XML序列化程序不应该生成无效的XML。

/ EDIT响应您的更新:向您的用户显示无效的XML进行编辑!而是在TextBox中显示正确转义的XML。修复损坏的XML并不好玩,我实际上没有理由不以有效的转义形式显示/编辑XML。

我再次问:如何在TextBox中显示XML?您似乎在某些时候故意忘记XML。

/编辑回复您的最新评论:是的,显然,因为它可以包含HTML。在将XML写入HTML页面之前,您需要正确地转义XML。有了它,我的意思是整个 XML。所以这个:

<foo mean-attribute="&lt;">

成为这个:

&lt;foo mean-attribute="&amp;&lt;"&gt;

答案 1 :(得分:5)

当然,当你将实体引用放在textarea中时,它们就会没有转义。 Textareas不是魔术,你必须逃避;你放入其中的所有东西就像其他元素一样。浏览器可能显示原始'&lt;'在textarea,但只是因为他们试图清理你的错误。

因此,如果您将可编辑的XML放在textarea中,则需要转义属性值一次以使其成为有效的XML,然后您必须再次转义整个XML以使其成为有效的HTML。您希望在页面中显示的最终来源是:

<textarea name="somexml">
    &lt;Configuration&gt;
        &lt;Validator Expression="[^&amp;lt;]" /&gt;
        &lt;Validator Expression="[^&amp;amp;]" /&gt;
    &lt;/Configuration&gt;
</textarea>

问题是基于对textarea元素的内容模型的误解 - 验证者会立即解决问题。

ETA评论:好吧,还有什么问题?这是序列化方面的问题。剩下的就是将其解析回来,为此你必须假设用户可以创建格式良好的XML。

尝试解析非格式良好的XML,以便允许出现'&lt;'这样的错误或'&amp;'未归属于属性值是一种损失,完全违背了XML应该如何工作。如果您不能信任您的用户编写格式良好的XML,请为它们提供一个更简单的非XML接口,例如一个简单的换行符分隔的regexp字符串列表。

答案 2 :(得分:1)

正如你所说,普通的序列化程序应该为你逃避一切。

问题是文本块:您需要自己处理通过文本块传递的任何内容。

您可以尝试使用HttpUtility.HtmlEncode(),但我认为最简单的方法是将您通过CDATA部分中的文本块的任何内容包住。

通常我当然希望所有内容都能正常转义,而不是依靠CDATA“拐杖”,但我还想使用内置工具进行转义。对于用户在其“休眠”状态下编辑的内容,我认为CDATA可能是最佳选择。

另见之前的问题:
Best way to encode text data for XML


<强>更新
基于对另一个响应的评论,我意识到你向用户显示标记,而不仅仅是内容。 Xml解析器很挑剔。我认为在这种情况下你能做的最好的事情就是在接受编辑的xml之前检查格式良好

也许尝试自动纠正某些类型的错误(例如我链接问题中的错误&符号),然后从.Net xml解析器获取第一个验证错误的行号和列号,并使用它来向用户显示他们的位置错误是直到他们给你一些可接受的东西。如果您还针对架构进行验证,则会获得奖励。

答案 3 :(得分:1)

您可以查看TinyMCE之类的内容,它允许您在富文本框中编辑html。如果您无法将其配置为完全符合您的要求,则可以将其用作灵感。

答案 4 :(得分:1)

注意:firefox(在我的测试中)不会像您描述的那样在文本区域中进行浏览。具体来说,这段代码:

<textarea cols="80" rows="10" id="1"></textarea>

<script>
elem = document.getElementById("1");

elem.value = '\
<Configuration>\n\
  <Validator Expression="[^&lt;]" />\n\
</Configuration>\
'
alert(elem.value);
</script>

被警告并显示给用户未更改,如:

<Configuration>
  <Validator Expression="[^&lt;]" />
</Configuration>

所以也许一个(不可行的?)解决方案是让你的用户使用firefox。


您的问题似乎已经揭晓了两个部分:

1 您显示的XML未转义。

例如,“&lt;”未转义为“&lt;”。但是因为“&lt;”也被称为“&lt;”,信息丢失,你无法取回它。

一种解决方案是让您转义所有“&”字符,以便“&lt;”变为“&amp;lt;”。然后,textarea将其转义为“&lt;”。当你读回来的时候,它就像它在第一时间一样。 (我假设textarea实际上改变了字符串,但是firefox没有按照你的报告行事,所以我不能检查这个)

另一个解决方案(我已经提到过)是建立/购买/借用一个自定义文本区域(如果简单,那就不错了,但是所有的编辑键,ctrl-C,ctrl-shift-left等等)。 / p>

2 您希望用户不必费心逃避。

你在逃避地狱:

正则表达式替换将主要起作用......但是当用户可能(合法地,在您给出的术语中)输入时,如何可靠地检测结束引用(“):

<Configuration>
  <Validator Expression="[^"<]" />
</Configuration>

从正则表达式语法的角度来看,它也无法判断最终的“是正则表达式的一部分,还是它的结尾。正则表达式语法通常用显式终结符来解决这个问题,例如:

/[^"<]/

如果用户使用了这种语法(带有终结符),并且你为它编写了一个解析器,那么你可以确定正则表达式何时结束,因此下一个“字符不是正则表达式的一部分,而是XML,以及哪些部分需要转义。我不是说你应该这样做!我说这在理论上是可行的。它远非快速和肮脏。

BTW:元素中的文本出现同样的问题。以下内容在您给出的条款中是合法的,但具有相同的解析问题:

<Configuration>
  <Expression></Expression></Expression>
</Configuration>

允许“任何文本”的语法中的基本规则是分隔符必须被转义(例如“或”),以便可以识别结尾。大多数语法也逃脱一堆其他的东西,为了方便/不方便。( EDIT 它需要逃脱转义字符本身:对于XML,它是“&”,当字面是转义为“&amp;”对于正则表达式,它是C / unix样式的“\”,当文字转义为“\\”时。

嵌套语法,你处于逃避地狱。

一个简单的解决方案就是告诉您的用户:这是一个快速配置编辑器,因此您无需任何幻想“无需逃脱“mamby-pamby:

  • 列出下一个字符并转义 到文本区域,例如:“&lt;”如 “&lt”。
  • 对于不会的XML 验证,再次向他们显示列表。

回顾过去,我看到bobince在我面前给出了相同的基本答案。

答案 5 :(得分:1)

在所有文本周围插入CDATA将为您提供另一种转义机制,该机制将(1)保存用户免于手动转义,以及(2)启用textarea自动转义的文本以便正确读回。

 <Configuration>
   <Validator Expression="<![CDATA[  [^<]   ]]>" />
 </Configuration>

: - )

答案 6 :(得分:0)

这个特殊字符 - “&lt;” - 应该替换为其他字符,以便您的XML有效。检查此链接是否包含XML特殊字符:

http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

在将TextBlock内容发送到反序列化程序之前,请尝试对其进行编码:

HttpServerUtility utility = new HttpServerUtility();
string encodedText = utility.HtmlEncode(text);

答案 7 :(得分:0)

这真的是我唯一的选择吗?这不是一个常见的问题,它在框架中的某个地方有解决方案吗?

private string EscapeAttributes(string configuration)
{
    var lt = @"(?<=\w+\s*=\s*""[^""]*)<(?=[^""]*"")";
    configuration = Regex.Replace(configuration, lt, "&lt;");

    return configuration;
}

(编辑:删除&符号替换,因为它导致问题往返)