2017-02-22 13 views
1

У меня есть требования по загрузке больших XML-файлов (от 0,5 МБ до 600 МБ), расшифровке данных, которые находятся в файле XML, и напишите, что к MemoryStream.Как передать (загрузить) XML-файл, изменить данные XML-элемента и записать в MemoryStream

Важно, чтобы дешифрованные данные не опирались на диск.

Ниже представлена ​​моя текущая реализация, которая загружает весь XML-документ в память, расшифровывает номер карты и устанавливает значение, а затем копирует измененный XML-документ в MemoryStream. Однако эта реализация не представляется возможной, поскольку она загружает весь XML-документ в ОЗУ.

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 элемента start/end, который я выполняю. По крайней мере, без разбора/загрузки всего элемента, который разрешает операция, например ReadOuterXml. Но я не хочу читать весь элемент. Я просто хочу написать исходный элемент-элемент в MemoryStream и обрабатывать только дешифрование номера карты, когда я сталкиваюсь с ними.

Обратите внимание, что номер карты находится в сериализованном объекте «транзакция». то есть <transaction>...<number>asdfa3423jasfa</number></transaction>

Итак, как я могу загрузить (передать) XML-файл, изменить биты данных в нем и постепенно записывать содержимое в MemoryStream?

+0

Для этого вам необходимо использовать потоковый XML API. Попробуйте XmlTextReader. Кроме того, вы знаете, что MemoryStream также все в памяти сразу, не так ли? – hoodaticus

+1

@hoodaticus yea в отношении MemoryStream, наше тестовое шоу, в котором прямое хранение MemoryStream в ОЗУ не является _as bad_, как загрузка всего XML в ОЗУ. Лестница вызывает исключения из-за памяти, тогда как MemoryStream в порядке и в наших ограничениях. – contactmatt

+0

не забудьте мое предложение XmlTextReader. Я использовал его для создания полностью потоковых сервисов - очень быстрое и маленькое использование плоской памяти было очень хорошим. – hoodaticus

ответ

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 на коэффициент в гнездовании.

+0

Работал как шарм. Немного изменил мои потребности в бизнесе, но это правильный ответ. – contactmatt

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

Смежные вопросы