提取部分文本,即JSON或XML

时间:2018-02-20 08:53:17

标签: c# json xml parsing

说,我们有以下文字:

Something1 { "prop": "value" } Something2 <?xml version="1.0" encoding="UTF-8"?>
<root><subnode /></root> Something3

是否有简单的方法将此字符串分解为多个部分:

  • 文字,Something1
  • JSON,{ "prop": "value" }
  • 文字,Something2
  • XML,<?xml version="1.0" encoding="UTF-8"?> <root><subnode /></root>
  • 文字,Something3

我只考虑JSON和XML格式。

目前我有两个想法:

  • 按字符解析字符串;如果字符是'{',请手动检查它是否是JSON(解析),如果是,请使用Newtonsoft JSON解析。如果字符为'&lt;',请手动检查它是否为XML(解析),如果是,则使用System.XML进行解析。缺点是我必须实现(简化)JSON和XML解析器,第二个可能是一个艰难的解析器(控制序列等)。
  • 很明显,当JSON和XML结束时,JSON和XML反序列化器应该能够说“可解析数据在这里结束”。如果是这样,当遇到'{'或'&lt;'时,我可以运行它们,提取,停止并继续。

我认为第一个选项是最后的选择 - 这是很多工作,我不确定我是否会涵盖所有JSON / XML警告。第二种可能(特别是使用System.XML和一些流行的JSON反序列化器,如Newtonsoft.JSON)?

或者有更好的方法来解决这个问题吗?

2 个答案:

答案 0 :(得分:0)

将此特定文本拆分为其部分很容易。编写一个程序,该程序采用与此类似的一组可能输入并对每个输入执行类似的处理是比较棘手的:因为它取决于&#34;类似的&#34;他们是。

指定类别&#34;类似&#34;输入是通过在BNF中编写语法来完成的,并且通过编写解析器来分割符合该语法的文本。这是所有完善的计算机科学,完全在任何本科课程中教授。写下你想要处理的一组消息的明确的BNF规范,其余的一帆风顺。

不幸的是,对于诸如JSON或XML之类的语言的解析器而言,通常的做法是将输入流保留在可以从JSON或XML完成的位置继续读取的状态(相反,它们将会如果在JSON或XML结构之外还有更多内容,则抛出错误。它有时是一个方便的功能,但它通常不可用。

答案 1 :(得分:0)

我设法实施了一个解决方案。简而言之,当它遇到{或&lt;字符,它使用Json或XML解析器从那时起解析它。如果Json或XML解析器抛出异常,我会提取发生异常的行和位置,然后剪切已处理字符串的新部分 - 从开始位置到异常指向的一个并尝试再次解析。如果我失败了,我只是接受{或&lt;作为一个普通人物继续前进。

我想它不会在每一个案例中都有效,有时它可能会产生意想不到的结果(例如XML中的JSON),但是对于我的需求来说已经足够了。

使用ValueTuple。您还必须为识别的部分实现自己的容器(SimpleTextPart,XmlTextPart,JsonTextPart)。

private int Move(string text, int current, int line, int position)
{
    while (current < text.Length && (line > 1 || position > 1))
    {
        if (text[current] == '\n')
        {
            if (line == 1)
                return -1;

            line--;
        }
        else if (line == 1)
        {
            position--;
        }

        current++;
    }

    return current;
}

private (bool jsonParseResult, BaseTextPart part, int newIndex) TryParseJson(string text, int current)
{
    try
    {
        string textPart = text.Substring(current);

        JObject obj = JObject.Parse(textPart);

        return (true, new JsonTextPart(obj), text.Length);
    }
    catch (JsonReaderException e)
    {
        int end = Move(text, current, e.LineNumber, e.LinePosition);

        try
        {
            string textPart = text.Substring(current, end - current);

            JObject obj = JObject.Parse(textPart);

            return (true, new JsonTextPart(obj), end);
        }
        catch (JsonReaderException)
        {
            return (false, null, 0);
        }
    }
}

private (bool xmlParseResult, BaseTextPart part, int newIndex) TryParseXml(string text, int current)
{
    XmlDocument doc = new XmlDocument();

    try
    {
        string textPart = text.Substring(current);

        doc.Load(new StringReader(textPart));    

        return (true, new XmlTextPart(doc), text.Length);
    }
    catch (XmlException e)
    {
        int end = Move(text, current, e.LineNumber, e.LinePosition);

        try
        {
            string textPart = text.Substring(current, end - current);

            doc.Load(new StringReader(textPart));

            return (true, new XmlTextPart(doc), end);
        }
        catch (XmlException)
        {
            return (false, null, 0);
        }
    }
}


private List<BaseTextPart> Parse(string text)
{
    var result = new List<BaseTextPart>();

    int current = 0;
    StringBuilder buffer = new StringBuilder();
    while (current < text.Length)
    {
        if (text[current] == '{')
        {
            (bool jsonParseResult, BaseTextPart part, int newIndex) = TryParseJson(text, current);

            if (jsonParseResult)
            {
                if (buffer.Length > 0)
                {
                    result.Add(new SimpleTextPart(buffer.ToString()));
                    buffer.Clear();
                }

                result.Add(part);
                current = newIndex;
                continue;
            }
        }

        if (text[current] == '<')
        {
            (bool xmlParseResult, BaseTextPart part, int newIndex) = TryParseXml(text, current);

            if (xmlParseResult)
            {
                if (buffer.Length > 0)
                {
                    result.Add(new SimpleTextPart(buffer.ToString()));
                    buffer.Clear();
                }

                result.Add(part);
                current = newIndex;
                continue;
            }
        }

        buffer.Append(text[current]);
        current++;
        continue;
    }

    if (buffer.Length > 0)
    {
        result.Add(new SimpleTextPart(buffer.ToString()));
        buffer.Clear();
    }

    return result;
}