2011-12-02 55 views
0

我試圖找到最好的解決方案,以防止我們在構建Xml文檔時分配太多內存。我必須使用較少的資源構建一個相當大的Xml(Web服務必須能夠每秒處理數百個調用)。 Xml本身的結構變化不大,但是數據一直在變化。 我目前的解決方案是XDocument和XElement(LINQ)。下面是我今天在做一個快速的示例:儘可能有效地寫模板化Xml流

static Stream GetXml(string d1, string d2, string d3) 
{ 
    XElement x = 
     new XElement("myElement", 
      new XElement("myOtherElement1", d1), 
      new XElement("myOtherElement2", d2), 
      new XElement("myOtherElement3", d3)); 
    // ... more XElement 
    // ... return Stream 
} 

當XML文件變得太大,實例化一個XDocument和數以百計的XElement變得非常昂貴和每秒滴的呼叫數量。 我目前正在考慮創建一些模板引擎,它只需簡單地串流字符串(XElement)而不需要實例化任何對象。你會怎麼做?這是正確的做法嗎?

static Stream GetXml(string d1, string d2, string d3) 
{ 
    const string xml = @" 
<myElement> 
    <myOtherElement1>{0}</myOtherElement1> 
    <myOtherElement2>{1}</myOtherElement2> 
    <myOtherElement3>{2}</myOtherElement3> 
</myElement>"; 

    // What's the best way to {0}, {1}, {2} without allocating 
    // objects and using as little RAM as possible. I cannot 
    // use string.Format since it allocates strings. 

    StreamWriter sw = new StreamWriter(stream); 
    sw.Write(xml); 
} 

回答

1

我可以解僱string.Format想到的唯一原因是,你不想讓整個XML文檔一次存儲在內存中。我寫了這個Stream類,該類只能將文檔的一小部分保存在內存中。

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Text; 

namespace StreamTest 
{ 
    public class EnumeratorStream : Stream 
    { 
     private readonly IEnumerator<string> source; 
     private readonly Encoding encoding; 

     public Encoding Encoding { get { return encoding; } } 

     private byte[] current = new byte[0]; 
     private int currentPos = 0; 

     public EnumeratorStream(IEnumerable<string> source, Encoding encoding) 
     { 
      if (source == null) throw new ArgumentNullException("source"); 
      if (encoding == null) encoding = Encoding.Default; 

      this.source = source.GetEnumerator(); 
      this.encoding = encoding; 
     } 

     private bool MoveNext() 
     { 
      while (source.MoveNext()) 
      { 
       if (source.Current.Length > 0) 
       { 
        current = encoding.GetBytes(source.Current); 
        currentPos = 0; 
        return true; 
       } 
      } 
      current = new byte[0]; 
      currentPos = 0; 
      return false; 
     } 

     #region Overrides of Stream 

     public override bool CanRead { get { return true; } } 
     public override bool CanSeek { get { return false; } } 
     public override bool CanWrite { get { return false; } } 

     public override int Read(byte[] buffer, int offset, int count) 
     { 
      if (buffer == null) throw new ArgumentNullException("buffer"); 
      if (offset < 0) throw new ArgumentOutOfRangeException("offset"); 
      if (offset + count > buffer.Length) throw new ArgumentException("Not enough buffer space"); 

      int totalWritten = 0; 
      while (count > 0) 
      { 
       int remaining = current.Length - currentPos; 
       if (remaining == 0 && !MoveNext()) break; 
       remaining = current.Length - currentPos; 
       if (remaining <= 0) break; 
       if (remaining > count) 
       { 
        remaining = count; 
       } 
       Array.Copy(current, currentPos, buffer, offset, remaining); 
       offset += remaining; 
       count -= remaining; 
       totalWritten += remaining; 
       currentPos += remaining; 
      } 
      return totalWritten; 
     } 

     public override void Flush() { } 
     public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } 
     public override void SetLength(long value) { throw new NotSupportedException(); } 
     public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } 
     public override long Length { get { throw new NotSupportedException(); } } 
     public override long Position 
     { 
      get { throw new NotSupportedException(); } 
      set { throw new NotSupportedException(); } 
     } 

     #endregion 
    } 
} 

例子:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Text; 

namespace StreamTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var stream = new EnumeratorStream(Generate("x","y","z"), null); 
      var buffer = new byte[256]; 
      int read; 
      while ((read = stream.Read(buffer,0,256)) > 0) 
      { 
       string s = stream.Encoding.GetString(buffer, 0, read); 
       Console.Write(s); 
      } 
      Console.ReadLine(); 
     } 

     public static IEnumerable<string> Generate(string d1, string d2, string d3) 
     { 
      yield return "<myElement>"; 
      yield return "<myOtherElement1>"; 
      yield return d1; 
      yield return "</myOtherElement1>"; 
      yield return "<myOtherElement2>"; 
      yield return d2; 
      yield return "</myOtherElement2>"; 
      yield return "<myOtherElement3>"; 
      yield return d3; 
      yield return "</myOtherElement3>"; 
      yield return "</myElement>"; 
     } 
    } 
} 
0

你可以傳遞一個StringBuilder。任何重複的字符串(比如打開和關閉標籤)都會引用內存中的相同數據,所以你會在那裏獲得一些節省。

static Stream GetXml(string d1, string d2, string d3) 
{ 
    StringBuilder xml = new StringBuilder(); 
    xml.Append("<myElement>"); 
    AppendElement(xml, d1); 
    AppendElement(xml, d2); 
    AppendElement(xml, d3); 
    xml.Append("</myElement>"); 

    // Create/return stream 
} 
static void AppendElement(StringBuilder xml, string value) 
{ 
    xml.Append("<myOtherElement>"); 
    xml.Append(value); 
    xml.Append("</myOtherElement>"); 
} 

爲了節省更多,你可以拼湊的開啓和關閉的元素是這樣的:

static Stream GetXml(string d1, string d2, string d3) 
{ 
    StringBuilder xml = new StringBuilder(); 
    OpenElement(xml, "myElement"); 
    AppendElement(xml, d1); 
    AppendElement(xml, d2); 
    AppendElement(xml, d3); 
    CloseElement(xml, "myElement"); 

    // Create/return stream 
} 
static void AppendElement(StringBuilder xml, string value) 
{ 
    OpenElement(xml, "myOtherElement"); 
    xml.Append(value); 
    CloseElement(xml, "myOtherElement"); 
}  

static void OpenElement(StringBuilder xml, string elementName) 
{ 
    xml.Append("<"); 
    xml.Append(elementName); 
    xml.Append(">"); 
} 
static void CloseElement(StringBuilder xml, string elementName) 
{ 
    xml.Append("</"); 
    xml.Append(elementName); 
    xml.Append(">"); 
} 
+0

這個選項的缺點是你所有的字符串不能保證是XML安全的。例如,如果值爲那麼您的串行器將流出<>。使用內置的XElement和其他XML友好的方法可以保護您免受這個問題的困擾。 –