如何将 xml 返回为 UTF-8 而不是 UTF-16

Posted

技术标签:

【中文标题】如何将 xml 返回为 UTF-8 而不是 UTF-16【英文标题】:How to return xml as UTF-8 instead of UTF-16 【发布时间】:2014-11-02 01:26:09 【问题描述】:

我正在使用序列化<T> 的例程。它可以工作,但是当下载到浏览器时,我看到一个空白页面。我可以查看页面源代码或在文本编辑器中打开下载并看到 xml,但它是 UTF-16 格式,我认为这就是浏览器页面显示空白的原因?

如何修改我的序列化程序以返回 UTF-8 而不是 UTF-16?

返回的 XML 源:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <string>January</string>
  <string>February</string>
  <string>March</string>
  <string>April</string>
  <string>May</string>
  <string>June</string>
  <string>July</string>
  <string>August</string>
  <string>September</string>
  <string>October</string>
  <string>November</string>
  <string>December</string>
  <string />
</ArrayOfString>

对序列化程序的示例调用:

DateTimeFormatInfo dateTimeFormatInfo = new DateTimeFormatInfo();
var months = dateTimeFormatInfo.MonthNames.ToList();

string SelectionId = "1234567890";

return new XmlResult<List<string>>(SelectionId)

    Data = months
;

序列化器:

public class XmlResult<T> : ActionResult

    private string filename = DateTime.Now.ToString("ddmmyyyyhhss");

    public T Data  private get; set; 

    public XmlResult(string selectionId = "")
    
        if (selectionId != "")
        
            filename = selectionId;
        
    

    public override void ExecuteResult(ControllerContext context)
    
        HttpContextBase httpContextBase = context.HttpContext;
        httpContextBase.Response.Buffer = true;
        httpContextBase.Response.Clear();

        httpContextBase.Response.AddHeader("content-disposition", "attachment; filename=" + filename + ".xml");
        httpContextBase.Response.ContentType = "text/xml";

        using (StringWriter writer = new StringWriter())
        
            XmlSerializer xml = new XmlSerializer(typeof(T));
            xml.Serialize(writer, Data);
            httpContextBase.Response.Write(writer);
        
    

【问题讨论】:

我认为这篇文章给了你你想要的东西:***.com/questions/22453036/… “它是 UTF-16 格式,我认为这就是为什么浏览器页面显示空白?” 我认为没有理由这么想。调查你的文件,它实际上是什么编码?开始时有任何 BOM 代码吗?等 【参考方案1】:

您可以使用强制使用 UTF8 的 StringWriter。这是一种方法:

public class Utf8StringWriter : StringWriter

    // Use UTF8 encoding but write no BOM to the wire
    public override Encoding Encoding
    
         get  return new UTF8Encoding(false);  // in real code I'll cache this encoding.
    

然后在您的代码中使用 Utf8StringWriter 编写器。

using (StringWriter writer = new Utf8StringWriter())

    XmlSerializer xml = new XmlSerializer(typeof(T));
    xml.Serialize(writer, Data);
    httpContextBase.Response.Write(writer);

答案的灵感来自Serializing an object as UTF-8 XML in .NET

【讨论】:

覆盖编码是否没有不必要的副作用?我不知道这可能会产生任何负面影响,但我对此有一种不好的感觉...... 我不知道,我过去在很多情况下都使用过它。但是对于服务器,我们根本不会在这种情况下使用字符串写入器,因为它会不必要地加倍缓冲。这就是我们在 MVC vNext 中所做的(在 Web API 中也是如此)github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Core/…github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Core/… Yishai,NobodysNightmare 的答案可以满足我的需要。在看到你的答案之前,我尝试了他的答案。也许你也给我指出了正确的方向。感谢您抽出宝贵时间提供帮助。 使用此技术时,您还需要实现默认构造函数,否则会出现错误。 @ITExpert 感谢您的指点,如果您可以扩展先前的评论以说明为什么需要这样做或错误是什么,这可能对其他用户更有帮助。【参考方案2】:

响应编码

我对框架的这一部分不太熟悉。但根据 MSDN,您可以像这样设置content encoding of an HttpResponse:

httpContextBase.Response.ContentEncoding = Encoding.UTF8;

XmlSerializer 看到的编码

再次阅读您的问题后,我发现这是困难的部分。问题在于StringWriter 的使用。由于 .NET 字符串始终存储为 UTF-16(需要引用 ^^),StringWriter 将其作为其编码返回。因此XmlSerializer 将 XML 声明写为

<?xml version="1.0" encoding="utf-16"?>

要解决这个问题,您可以像这样写入 MemoryStream:

using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))

    XmlSerializer xml = new XmlSerializer(typeof(T));
    xml.Serialize(writer, Data);

    // I am not 100% sure if this can be optimized
    httpContextBase.Response.BinaryWrite(stream.ToArray());

其他方法

另一个编辑:我刚刚注意到 jtm001 链接的this SO answer。浓缩的解决方案是为XmlSerializer 提供一个自定义XmlWriter,该XmlWriter 配置为使用UTF8 作为编码。

Athari proposes 派生自 StringWriter 并将编码宣传为 UTF8。

据我了解,这两种解决方案都应该可以工作。我认为这里的要点是您将需要一种样板代码或另一种......

【讨论】:

这个答案的缺点是,对于大型 XML 响应,您现在将它们全部写入内存,这将导致可能不需要的大量内存消耗,如果您超过 85KB,您的响应将进入大对象堆。当这种情况经常发生时,您的应用可能会在垃圾收集期间开始冻结。 ".NET uses the UTF-16 encoding ... to represent characters and strings"(引文)【参考方案3】:

序列化为 UTF8 字符串:

    private string Serialize(MyData data)
    
        XmlSerializer ser = new XmlSerializer(typeof(MyData));
        // Using a MemoryStream to store the serialized string as a byte array, 
        // which is "encoding-agnostic"
        using (MemoryStream ms = new MemoryStream())
            // Few options here, but remember to use a signature that allows you to 
            // specify the encoding  
            using (XmlTextWriter tw = new XmlTextWriter(ms, Encoding.UTF8)) 
            
                tw.Formatting = Formatting.Indented;
                ser.Serialize(tw, data);
                // Now we get the serialized data as a string in the desired encoding
                return Encoding.UTF8.GetString(ms.ToArray());
            
    

要在 Web 响应中将其作为 XML 返回,请不要忘记设置响应编码:

    string xml = Serialize(data);
    Response.ContentType = "application/xml";
    Response.ContentEncoding = System.Text.Encoding.UTF8;
    Response.Output.Write(xml);

【讨论】:

以上是关于如何将 xml 返回为 UTF-8 而不是 UTF-16的主要内容,如果未能解决你的问题,请参考以下文章

UTF-8 和 UTF-16 之间是不是存在巨大差异

python requests.get()返回不正确解码的文本而不是UTF-8?

如何将整个 MySQL 数据库字符集和排序规则转换为 UTF-8?

如何使用 XML/SGML 实体将 UTF-16 转换为 ASCII/ANSI?

将Base64解码为UTF-8而不是单字节编码文本

使用 XSLT 如何让我的输出重复而不是只返回第一个实例?