Преобразование C # XSLT из памяти - PullRequest
2 голосов
/ 23 сентября 2010

Все,

У меня есть следующий код для преобразования XML-документа с использованием XSLT. Проблема в том, что, когда размер XML-документа составляет около 12 МБ, в C # не хватает памяти. Есть ли другой способ выполнить преобразование, не занимая столько памяти?

public string Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)
    {
        try
        {
            var stm = new MemoryStream();
            myXslTrans.Transform(myXPathDoc, null, stm);
            var sr = new StreamReader(stm);
            return sr.ReadToEnd();
        }
        catch (Exception e)
        {
          //Log the Exception
        }
    }

Вот трассировка стека:

at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32       length, Int32 capacity)
at System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)   
at System.Text.StringBuilder.Append(Char[] value, Int32 startIndex, Int32 charCount)
at System.IO.StreamReader.ReadToEnd()
at Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)

Ответы [ 6 ]

4 голосов
/ 24 сентября 2010

Первое, что я бы сделал, это изолировал проблему.Вытащите весь бизнес MemoryStream из игры и передайте вывод в файл, например:

using (XmlReader xr = XmlReader.Create(new StreamReader("input.xml")))
using (XmlWriter xw = XmlWriter.Create(new StreamWriter("output.xml")))
{
   xslt.Transform(xr, xw);
}

Если вы все еще получаете исключение нехватки памяти (я бы поспорил, что вы будете складывать деньги),это довольно убедительное указание на то, что проблема не в размере выходных данных, а в чем-то в самом преобразовании, например, в том, что бесконечно повторяется, например:

<xsl:template match="foo">
   <bar>
      <xsl:apply-templates select="."/>
   </bar>
</xsl:template>
3 голосов
/ 23 сентября 2010

MemoryStream + ReadToEnd означает, что вам нужно 2 копии в памяти в этот момент.Вы можете оптимизировать это до 1 копии, используя объект StringWriter в качестве цели (заменив MemStream + Reader), и использовать writer.ToString (), когда вы закончите.

Но это даст вам в лучшем случае только до 24 МБ, но все еще слишком мало.Должно быть что-то еще.
Невозможно сказать, что, возможно, ваш XSLT слишком сложный или неэффективный.


var writer = new StringWriter();
//var stm = new MemoryStream();
myXslTrans.Transform(myXPathDoc, null, writer);
//var sr = new StreamReader(stm);
//return sr.ReadToEnd();
return writer.ToString();
2 голосов
/ 23 сентября 2010

Вам нужно

stm.Position = 0

для сброса потока памяти в начало перед чтением содержимого с помощью StreamReader. В противном случае вы пытаетесь прочитать содержимое из конца потока.

0 голосов
/ 13 января 2011

Убедитесь, что у вас нет JavaScript, иначе есть известная утечка памяти.

Мой ответ имеет силу и может избежать многих ошибок и утечек памяти.Пользователь отказался от меня, потому что он не понимал, что JavaScript может быть встроен в XSLT как расширение.

Вот старая статья, которая объясняет, как это сделать.http://msdn.microsoft.com/en-us/magazine/cc302079.aspx

.Net-классы, размещенные на веб-сервере, имеют известные утечки памяти при использовании класса XslTransform, когда JavaScript встроен в документ XSLT через расширение.JavaScript использовался для получения таких данных, как даты, и для более динамичной обработки.Вот почему я даю предупреждение тем, кто использует расширение JavaScript.Это наиболее вероятная причина утечки памяти.

Другим предупреждением является осторожность при использовании более нового класса XslCompliedTransform.С моими большими документами XSLT я профилировал процессор в 4 раза больше класса XslTransform и в два раза больше его памяти.

0 голосов
/ 23 сентября 2010

Может быть или не быть связанным, но вам нужно убедиться, что вы утилизируете объекты потока и читателя. Я также добавил в позиции = 0, на которую указал Ник Джонс.

public string Transform(XPathDocument myXPathDoc, XslCompiledTransform myXslTrans)
{
    try
    {
        using (var stm = new MemoryStream())
        {
             myXslTrans.Transform(myXPathDoc, null, stm);
             stm.Position = 0;
             using (var sr = new StreamReader(stm))
             {
                 return sr.ReadToEnd();
             }
        }
    }
    catch (Exception e)
    {
        //Log the Exception
    }
}
0 голосов
/ 23 сентября 2010

Функция ReadToEnd () загружает весь поток в память.Вам лучше использовать XmlReader для потоковой передачи документа по частям, а затем запустить xslt для небольших фрагментов.Вы также можете рассмотреть возможность передачи документа полностью с XmlReader и не использовать xslt, который менее подходит для потоковой передачи данных и менее масштабируем для больших файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...