2011-11-24 21 views
13

.NET的XmlTextWriter創建無效的xml文件。XmlTextWriter錯誤地編寫控制字符

在XML中,允許一些控制字符,如'水平製表符'(	),但其他控制字符不是,如'垂直製表符'()。 (請參閱spec。)

我有一個字符串,其中包含XML中不允許的UTF-8控制字符。
雖然XmlTextWriter轉義字符,結果XML仍然是無效的。

如何確保XmlTextWriter從不產生非法XML文件?

或者,如果無法通過XmlTextWriter執行此操作,如何從字符串中去除XML中不允許的特定控制字符?

示例代碼:

using (XmlTextWriter writer = 
    new XmlTextWriter("test.xml", Encoding.UTF8)) 
{ 
    writer.WriteStartDocument(); 
    writer.WriteStartElement("Test"); 
    writer.WriteValue("hello \xb world"); 
    writer.WriteEndElement(); 
    writer.WriteEndDocument(); 
} 

輸出:

<?xml version="1.0" encoding="utf-8"?><Test>hello &#xB; world</Test> 
+0

你不能在XML中有一個轉義的垂直標籤?你能參考標準嗎? – Jodrell

+0

@Jodrell沒錯,你做不到。 XML用於文本,不用於控制字符或二進制數據。 http://www.w3.org/TR/REC-xml/#charsets – jasso

回答

10

一個行爲的本文檔隱藏在documentation of the WriteString method但聽起來好像它適用於整個類。

一個的XmlWriter的默認行爲使用創建創建是拋出 一個ArgumentException試圖寫入在 範圍0X-0x1F的字符值(不包括空白字符0x9,是0xA,和爲0xD)時。 可以通過創建CheckCharacters屬性設置爲false的XmlWriter 來編寫這些無效的XML字符。這樣做會導致 中的字符被數字字符實體替代(&#0;&#0x1F)。此外,默認情況下,使用新的 運算符創建的XmlTextWriter將用數字字符 實體替換無效字符。

因此,看來您最終會因爲使用XmlTextWriter類而寫入無效字符。更好的解決方案是使用XmlWriter Class代替。

+0

這有點奇怪,但顯然即使'XmlTextWriter'構造函數存在,你也不應該使用它:http:// msdn。 microsoft.com/en-us/library/kkz7cs0d.aspx –

1

建在.NET逃生者,如SecurityElement.Escape不正確逃脫/剝離它。

  • 您可以設置CheckCharactersfalse的作家和讀者都如果你的應用是唯一一個與文件交互。儘管如此,生成的XML文件在技術上仍然是無效

參見:

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); 
xmlWriterSettings.Encoding = new UTF8Encoding(false); 
xmlWriterSettings.CheckCharacters = false; 
var sb = new StringBuilder(); 
var w = XmlWriter.Create(sb, xmlWriterSettings); 
w.WriteStartDocument(); 
w.WriteStartElement("Test"); 
w.WriteString("hello \xb world"); 
w.WriteEndElement(); 
w.WriteEndDocument(); 
w.Close(); 
var xml = sb.ToString(); 
  • 如果設置CheckCharacterstrue(這是默認設置)是有點過於嚴格,因爲它會簡單地拋出一個異常的替代方法,它更寬鬆無效的XML字符將只是去除他們:

谷歌搜索有點產生白名單XmlTextEncoder但是它也會刪除DEL以及U + 007F-U + 0084,U + 0086-U + 009F範圍內的其他人,根據維基百科上的Valid XML Characters僅在特定上下文中有效,並且RFC提及爲鼓勵但仍然有效的字符。

public static class XmlTextExtentions 
{ 
    private static readonly Dictionary<char, string> textEntities = new Dictionary<char, string> { 
     { '&', "&amp;"}, { '<', "&lt;" }, { '>', "&gt;" }, 
     { '"', "&quot;" }, { '\'', "&apos;" } 
    }; 
    public static string ToValidXmlString(this string str) 
    { 
     var stripped = str 
      .Select((c,i) => new 
      { 
       c1 = c, 
       c2 = i + 1 < str.Length ? str[i+1]: default(char), 
       v = XmlConvert.IsXmlChar(c), 
       p = i + 1 < str.Length ? XmlConvert.IsXmlSurrogatePair(str[i + 1], c) : false, 
       pp = i > 0 ? XmlConvert.IsXmlSurrogatePair(c, str[i - 1]) : false 
      }) 
      .Aggregate("", (s, c) => {     
       if (c.pp) 
        return s; 
       if (textEntities.ContainsKey(c.c1)) 
        s += textEntities[c.c1]; 
       else if (c.v) 
        s += c.c1.ToString(); 
       else if (c.p) 
        s += c.c1.ToString() + c.c2.ToString(); 
       return s; 
      }); 
     return stripped; 
    } 
} 

這通過了所有測試XmlTextEncoder不同的是期望它剝去一個DEL其中XmlConvert.IsXmlChar,Wikipedia和規範標記爲有效的(雖然泄氣)字符。

3

剛剛發現這個問題時,我用同樣的問題掙扎,我結束了一個正則表達式解決它:

return Regex.Replace(s, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", ""); 

希望它可以幫助別人作爲一種替代解決方案。