我有一个由连接的有效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。
答案 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
}
}
有几点需要注意。