如何从文件中有效地解析连接的XML文档

时间:2009-08-24 12:15:48

标签: java xml parsing

我有一个由连接的有效XML文档组成的文件。我想有效地分离单个XML文档。

连接文件的内容如下所示,因此连接文件本身不是有效的XML文档。

<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>

每个单独的XML文档大约1-4 KB,但可能有几百个。所有XML文档都对应于相同的XML Schema。

有任何建议或工具吗?我在Java环境中工作。

编辑:我不确定xml声明是否会出现在文档中。

编辑:我们假设所有xml文档的编码都是UTF-8。

5 个答案:

答案 0 :(得分:4)

不要拆分!在它周围添加一个大标签!然后它再次成为一个XML文件:

<BIGTAG>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
</BIGTAG>

现在,使用/ BIGTAG / SomeData将为您提供所有XML根。

<小时/> 如果处理说明有问题,您可以随时使用RegEx删除它们。删除所有处理指令比使用RegEx查找所有根节点更容易。 如果所有文档的编码都不同,那么请记住:整个文档本身必须由某种编码类型编码,因此它包含的所有那些XML文档都将使用相同的编码,无论每个标题告诉您什么。如果大文件编码为UTF-16,那么XML处理指令是否说XML本身是UTF-8并不重要。它不是UTF-8,因为整个文件是UTF-16。因此,这些XML处理指令中的编码无效。

通过将它们合并到一个文件中,您已经改变了编码...

<小时/> 通过RegEx,我的意思是正则表达式。你只需要删除一个&lt ;?之间的所有文本。和?>如果你正在尝试其他的字符串操作技术,那么使用正则表达式应该不会太难,而且稍微复杂一些。

答案 1 :(得分:3)

由于您不确定声明是否始终存在,您可以删除所有声明(<\?xml version.*\?>等正则表达式可以找到这些声明),前置<doc-collection>,附加</doc-collection>,这样生成的字符串将是一个有效的xml文档。在其中,您可以使用(例如)XPath查询/doc-collection/*来检索单独的文档。如果组合文件足够大以至于内存消耗成为问题,则可能需要使用Sax等流式解析器,但原理保持不变。

在我遇到的类似场景中,我只是直接使用xml-parser读取连接文档:虽然连接文件可能不是有效的xml 文档,但它是有效的xml fragment (禁止重复声明) - 因此,一旦剥离声明,如果解析器支持解析片段,那么您也可以直接读取结果。然后,所有顶级元素将成为连接文档的根元素。

简而言之,如果你删除所有声明,你将拥有一个有效的xml片段,它可以直接解析或用一些标记包围它。

答案 2 :(得分:3)

正如Eamon所说,如果你知道&lt;?xml&gt;事情将永远存在,只要打破它。

如果失败,请查找结束文档级标记。也就是说,扫描文本计算你的深度。每当您看到以“&lt;”开头的标记时但不是“&lt; /”并且不以“/&gt;”结尾,请将深度计数加1。每当您看到以“&lt; /”开头的标记时,减去1.每次减去1,检查您现在是否为零。如果是这样,您已到达XML文档的末尾。

答案 3 :(得分:1)

这是我对C#版本的回答。非常难看的代码: - \

public List<T> ParseMultipleDocumentsByType<T>(string documents)
    {
        var cleanParsedDocuments = new List<T>();
        var serializer = new XmlSerializer(typeof(T));
        var flag = true;
        while (flag)
        {
            if(documents.Contains(typeof(T).Name))
            {
                var startingPoint = documents.IndexOf("<?xml");
                var endingString = "</" +typeof(T).Name + ">";
                var endingPoing = documents.IndexOf(endingString) + endingString.Length;
                var document = documents.Substring(startingPoint, endingPoing - startingPoint);
                var singleDoc = (T)XmlDeserializeFromString(document, typeof(T));
                cleanParsedDocuments.Add(singleDoc);
                documents = documents.Remove(startingPoint, endingPoing - startingPoint);
            }
            else
            {
                flag = false;
            }
        }


        return cleanParsedDocuments;
    }

    public static object XmlDeserializeFromString(string objectData, Type type)
    {
        var serializer = new XmlSerializer(type);
        object result;

        using (TextReader reader = new StringReader(objectData))
        {
            result = serializer.Deserialize(reader);
        }

        return result;
    }

答案 4 :(得分:0)

我没有Java答案,但这是我用C#解决这个问题的方法。

我创建了一个名为XmlFileStreams的类来扫描源文档以获取XML文档声明,并将其逻辑分解为多个文档:

class XmlFileStreams {

    List<int> positions = new List<int>();
    byte[] bytes;

    public XmlFileStreams(string filename) {
        bytes = File.ReadAllBytes(filename);
        for (int pos = 0; pos < bytes.Length - 5; ++pos)
            if (bytes[pos] == '<' && bytes[pos + 1] == '?' && bytes[pos + 2] == 'x' && bytes[pos + 3] == 'm' && bytes[pos + 4] == 'l')
                positions.Add(pos);
        positions.Add(bytes.Length);
    }

    public IEnumerable<Stream> Streams {
        get {
            if (positions.Count > 1)
                for (int i = 0; i < positions.Count - 1; ++i)
                    yield return new MemoryStream(bytes, positions[i], positions[i + 1] - positions[i]);
        }
    }

}

使用XmlFileStreams:

foreach (Stream stream in new XmlFileStreams(@"c:\tmp\test.xml").Streams) {
    using (var xr = XmlReader.Create(stream, new XmlReaderSettings() { XmlResolver = null, ProhibitDtd = false })) {
        // parse file using xr
    }
}

有几点需要注意。

  1. 将整个文件读入内存进行处理。如果文件非常大,这可能是个问题。
  2. 它使用简单的强力搜索来查找XML文档边界。