如何流式传输(加载)XML文件,修改XML元素数据以及写入MemoryStream

时间:2017-02-22 17:32:47

标签: c# xml xml-parsing

我要求加载大型XML文件(介于0.5MB - 600MB之间),解密XML文件中的数据,并将其写入MemoryStream。

解密的数据不会停留在磁盘上非常重要。

下面是我当前的实现,它将整个XML文档加载到内存中,解密卡号并设置值,然后将修改后的XML文档复制到MemoryStream。但是,这种实现方式并不可行,因为它将整个XML文档加载到RAM中。

public MemoryStream DecryptFile(string xmlFullPath, DateTime encryptionKey)
{
    XNamespace xmlNameSpace = "http://www.xml.com/schema";

    XDocument fileXDocument = XDocument.Load(xmlFullPath);

    IEnumerable<XElement> cardElements =
        fileXDocument
        .Descendants(xmlNameSpace + "card");

    // Iterate over each <card> element within the <batchRequest>.
    foreach (XElement cardElement in cardElements)
    {
        XElement cardNumberElement = cardElement.Element(xmlNameSpace + "number");

        // Read encrypted value
        // Decrypt value

        cardNumberElement.SetValue(decryptedCreditCard);
    }

    // Save the XML document, with the decrypted cards, to a memory stream.
    var memoryStream = new MemoryStream();

    fileXDocument.Save(memoryStream, SaveOptions.DisableFormatting);

    // Rewind the stream, so that it's ready to be read from it elsewhere.
    memoryStream.Position = 0;

    return memoryStream;
}

我非常熟悉XmlReader,我将其用于其他操作。

我已经考虑过能够运行XML文档,并简单地将逐个元素写入相应的MemoryStream,并最终解密数据并将其写入内存流,因为卡号出现

但是,我无法获取我正在迭代的开始/结束元素的原始XML。至少,没有解析/加载整个元素,像ReadOuterXml这样的操作允许。但我不想阅读整个元素。我只是想将原始元素逐个元素写入MemoryStream,并且只处理解密卡号码时遇到的问题。

请注意,卡号在序列化对象&#34; transaction&#34;中。即<transaction>...<number>asdfa3423jasfa</number></transaction>

那么,我如何加载(流)XML文件,修改其中的数据位,并逐步将内容写入MemoryStream?

2 个答案:

答案 0 :(得分:1)

您必须使用XmlReader阅读并使用XmlWriter撰写所有内容。请记住,这是一个仅向前游标,因此您必须存储处理它时需要的任何内容。

下面是一个示例函数,用于执行类似于您需要的操作。

public static MemoryStream DecryptFile(string xmlFullPath, DateTime encryptionKey) {
    var elemToLook = "number";
    var inElem = false;
    var number = "";
    var memoryStream = new MemoryStream();
    using (var writer = XmlWriter.Create(memoryStream))
    using (var reader = XmlReader.Create(xmlFullPath)) {
        while (reader.Read()) {
            switch (reader.NodeType) {
                case XmlNodeType.Element:
                    if (reader.Name == elemToLook)
                        inElem = true;
                    writer.WriteStartElement(reader.Name);
                    break;
                case XmlNodeType.Text:
                    if (inElem) {
                        number = reader.Value;
                        // TODO: This is where your decryption code will go.
                        number = $"decrypted({number})"; 
                        writer.WriteString(number);
                    } else
                        writer.WriteString(reader.Value);
                    break;
                case XmlNodeType.XmlDeclaration:
                case XmlNodeType.ProcessingInstruction:
                    writer.WriteProcessingInstruction(reader.Name, reader.Value);
                    break;
                case XmlNodeType.Comment:
                    writer.WriteComment(reader.Value);
                    break;
                case XmlNodeType.EndElement:
                    if (inElem)
                        inElem = false;
                    writer.WriteFullEndElement();
                    break;
                case XmlNodeType.Whitespace:
                    writer.WriteRaw(reader.Value);
                    break;
            }
        }
    }

    memoryStream.Position = 0;
    return memoryStream;
}

我建议您传入一个Action委托进行处理,以便将自定义处理与标准XML处理分开

此外,如果您只想在<number>... </number>嵌套在<transaction>...</transaction>等其他标签内时阅读inElem,那么在设置li标志时需要处理在筑巢中。

答案 1 :(得分:0)

要处理XML流而不是整个文档,您可以使用XmlTextReader来阅读流,然后使用相应的XmlTextWriter将其放入目标MemoryStream

可在此处找到这些课程的文档:

XmlTextReader的 https://msdn.microsoft.com/en-us/library/system.xml.xmltextreader(v=vs.110).aspx

的XmlTextWriter https://msdn.microsoft.com/en-us/library/system.xml.xmltextwriter(v=vs.110).aspx