将带有中文字符的XML发送到Microsoft Translator API会引发反序列化异常



我正在尝试使用Microsoft Translator API将中文(简体)翻译成英文。

几个要求

  • 我必须使用HTTP方法POST,而不是GET与查询字符串,因为我的查询超过了微软的URI限制15,845个字符(注意,这是可能的,即使我使用少于10,000个字符的情况下,中文字符的限制。原因是查询字符串必须经过URL编码,这大大增加了长度,但在确定字符数之前,它由Microsoft解码。

  • 唯一允许POST s的HTTP转换方法是TranslateArrayMethod,例如TranslateMethod只允许GET s。不幸的是,TranslateArrayMethod只接受XML文档,所以我必须使用XML。

下面是我发送的XML文档的示例:

<TranslateArrayRequest>
    <AppId/>
    <From>es</From>
    <Options>
        <ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
    </Options>
    <Texts>
        <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <![CDATA[Hola]]>
        </string>
    </Texts>
    <To>en</To>
</TranslateArrayRequest>

这很好,结果是:

<ArrayOfTranslateArrayResponse xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<TranslateArrayResponse>
    <From>es</From>
    <OriginalTextSentenceLengths xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <a:int>4</a:int>
</OriginalTextSentenceLengths>
<TranslatedText>Hello</TranslatedText>
<TranslatedTextSentenceLengths xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>5</a:int>
</TranslatedTextSentenceLengths>
</TranslateArrayResponse>
</ArrayOfTranslateArrayResponse>

但是,如果我接着添加任何中文字符,像这样:

<TranslateArrayRequest>
    <AppId/>
    <From>zh-CHS</From>
    <Options>
        <ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
    </Options>
    <Texts>
        <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <![CDATA[南]]>
        </string>
    </Texts>
    <To>en</To>
</TranslateArrayRequest>

我得到一个奇怪的回应:

<html>
    <body/>
    <h1>System.Runtime.Serialization.SerializationException</h1>
    <p>Message: There was an error deserializing the object of type Microsoft.MT.MDistributor.V2.TranslateArrayRequest. Unexpected end of file. Following elements are not closed: TranslateArrayRequest. Line 1, position 298.</p>
</html>

请注意,我也尝试不使用CDATA转义,但它没有帮助。修改From语言也没有效果。

我正在使用Node.js (Javascript),尽管由于这是一个通用的HTTP API,我认为这应该无关紧要。

好吧,我在从Node.js调用Microsoft Translator POST api时遇到了完全相同的问题。只要没有非ascii字符,API就可以正常工作-返回预期的翻译,但是当我向POST主体的适当<string>部分添加单个重音' '字符时,它会响应一个错误:

    <html><body/><h1>System.Runtime.Serialization.SerializationException</h1>
<p>Message: There was an error deserializing the object of type Microsoft.MT.MDistributor.V2.TranslateArrayRequest. Unexpected end of file. Following elements are not closed: TranslateArrayRequest. Line 1, position 782.</p>
</html>

我发现问题是Content-Length头希望以字节为单位的长度,但我一直在发送字符长度。为什么会发生这种情况?衡量Node http请求正文长度的典型方法是调用

var length = body.length

并获取字符串的"长度",即字符数。当所有字符都是ASCII时,此操作有效。然而,事实证明,在UTF-8中,非ascii字符(包括我的重音' ')每个可以超过一个字节。因此,当正文包含非ascii字符时,字节长度将不再等于字符长度,而字符长度是不正确的。在本例中,它导致Microsoft服务器过早停止读取消息,从而生成错误消息。

相反,我们需要用调用(在Node.js中)

来测量字节长度。
var length = Buffer.byteLength(body, 'utf8')

并在Content-Length头中发送该长度,Microsoft Translator API再次工作。

问题很可能不是中文,而是微软翻译不喜欢新的行符号。当我遇到这个错误消息时,我做了如下更改:

  1. 节点的每个内容中,用空字符串替换换行符。这些字符具有Unicode值:0xA, 0xB, 0xC, 0xD, 0x85, 0x2028, 0x2029
  2. 节点的每个内容中,用它们的替代表示替换XML保留字:

    ,→,amp;

    & lt;→,lt;

    比;→和gt;

    '→'

    →"

  3. 将整个XML重新排列成单行

之后,一切都很顺利。关于你的特殊例子,符号"中国"被翻译成"南方"。我没有使用CDATA转义

最新更新