如何判断一个字符串是否为XML格式?

13
我们有一个字符串字段,其中可能包含XML或纯文本。该XML不包含<?xml头和根元素,即不是格式良好的XML。
我们需要能够编辑XML数据,清空元素和属性值,仅保留它们的名称,因此在编辑之前需要测试这个字符串是否为XML。
目前我正在使用以下方法:
string redact(string eventDetail)
{
    string detail = eventDetail.Trim();
    if (!detail.StartsWith("<") && !detail.EndsWith(">")) return eventDetail;
    ...

有更好的方法吗?

这种方法可能会忽略哪些特殊情况?

我知道可以使用XmlDocument.LoadXml并捕获XmlException,但这感觉像是一个昂贵的选择,因为我已经知道很多数据不会是 XML 格式。

这是一个 XML 数据的示例,除了缺少根元素(为了节省空间而省略),我们可以假设它是格式良好的:

<TableName FirstField="Foo" SecondField="Bar" /> 
<TableName FirstField="Foo" SecondField="Bar" /> 
...

目前我们只使用基于属性的值,但如果数据变得更加复杂,我们将来可能会使用元素。

解决方法

根据多个评论(谢谢!)。

string redact(string eventDetail)
{
    if (string.IsNullOrEmpty(eventDetail)) return eventDetail; //+1 for unit tests :)
    string detail = eventDetail.Trim();
    if (!detail.StartsWith("<") && !detail.EndsWith(">")) return eventDetail;
    XmlDocument xml = new XmlDocument();
    try
    {
        xml.LoadXml(string.Format("<Root>{0}</Root>", detail));
    }
    catch (XmlException e)
    {
        log.WarnFormat("Data NOT redacted. Caught {0} loading eventDetail {1}", e.Message, eventDetail);
        return eventDetail;
    }
    ... // redact

我会选择使用LoadXml,这样你就知道输入的“XML”数据是有效的。如果你使用你的方法(代码),你可能会有格式不正确的XML通过测试。 - Martin
请参见 https://dev59.com/S0jSa4cB1Zd3GeqPD0AD 。 - Graviton
你是自己写的吗?我不太明白为什么你要以一种无法正确解释区域的方式来编写它,然后呢...? - Noon Silk
谢谢大家,Ngu和RRUZ找到了非常相似的问题,所以我会删除这个。 - si618
@Silky,这些数据是用于我们的审计日志,很多事件细节都是关于“密码错误”,“用户名错误”,“会话超时”等等。其中一部分(XML)是用于查找数据 - 用户搜索了什么以及返回了什么。你说得对,我可以使用另一种方法来确定是否需要期望XML,但它必须依赖于代码,并且我们不断添加更多的代码,它也不会100%可靠,因为我无法控制其他程序员写入日志的内容。 - si618
显示剩余3条评论
7个回答

8

如果您首先要接受格式不正确的XML,我认为捕获异常是处理它的最佳方式。


我已经更新了问题,并提供了示例数据。我们可以假设它是格式良好的,除了缺少根元素。 - si618

4

一种可能的解决方案是将两种方法结合使用。您可以使用您的编辑方法,并尝试在if语句中加载它。这样,您只会尝试加载可能是格式良好的xml,并且会丢弃大部分非xml条目。


我将此标记为最合适的答案,因为我认为它以最有效的方式解决了我的问题。对于大多数情况,StartsWith < 和 EndsWith > 将过滤掉非 XML 数据,而对于像 Ira Baxter 描述的罕见情况,捕获 XmlException 将解决这些问题。 - si618

2

如果您的目标是可靠性,那么最好的选择是使用XmlDocument.LoadXml来确定它是否为有效的XML。对数据进行完全解析可能很昂贵,但这是唯一可靠地判断它是否为有效的XML的方法。否则,您没有检查缓冲区中未检查的任何字符都可能导致数据成为非法的XML。


我认为在这里使用XmlDocument不是一个好选择 - 他不需要DOM,只需要验证。看起来XmlReadertry { while (reader.Read(); } catch(XmlException ex) { ... }会是一种更轻量级的方法。 - Pavel Minaev
@Pavel,但我还需要修改Xml以编辑数据,因此需要XmlDocument。 - si618
同意,但如果我结合Samuel的想法,使用StartsWith和EndsWith代码可以捕获99%的纯文本,并将其他1%留给LoadXml抛出XmlException时捕获。 - si618

1

这取决于您想要多准确的测试。考虑到您已经没有官方的<xml,您已经在尝试检测不是XML的东西。理想情况下,您应该通过完整的XML解析器(如您建议的LoadXML)解析文本;任何被拒绝的内容都不是XML。问题是,您是否在意接受非XML字符串?例如,您是否接受

  <the quick brown fox jumped over the lazy dog's back>

你是将其作为XML并剥离吗?如果是,那么你的技术是好的。如果不是,你需要决定你想要多严格的测试,并编写一个具有该程度严格性的识别器。


是的,那就是我担心遇到的问题。 - si618

1

数据是如何传递给您的?周围还有什么其他类型的数据?也许有更好的方法;也许您可以对您控制的数据进行标记化,然后推断出不在这些标记内的任何内容都是XML,但我们需要了解更多信息。

如果没有像那样可爱的解决方案,我认为您现在的做法是可以的(用于验证它以这些字符开头和结尾)。

我们确实需要更多关于数据格式的信息。


0
如果XML不包含根元素(即它是一个XML片段,而不是完整文档),那么下面的示例也是完全有效的,但不会匹配您的探测器:
foo<bar/>baz

实际上,任何文本字符串都可以作为有效的XML片段(考虑一下如果原始的XML文档只是将一些文本包装在根元素中,然后您将根元素标记去掉)。

0
try
{
    XmlDocument myDoc = new XmlDocument();
    myDoc.LoadXml(myString);
}
catch(XmlException ex)
{
    //take care of the exception
}

1
当然,在问题中已经说明了这一点。但是,当我知道很多数据不是XML格式时,捕获异常的代价就会很高。 - si618

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