Размещение XML с китайскими символами в Microsoft Translator API вызывает исключение десериализации
Я пытаюсь перевести с китайского (упрощенного) на английский, используя Microsoft Translator API.
Пара требований
Я должен использовать метод HTTP
POST
, и неGET
со строкой запроса, потому что мои запросы превышают предел URI Microsoft в 15 845 символов (обратите внимание, что это возможно, даже если я использую ограничение менее 10000 символов в случае китайских символов. Причина в том, что строка запроса должна быть закодирована в URL, который значительно увеличивает длину, но он декодируется Microsoft до определения количества символов.Единственный метод HTTP-перевода, который позволяет
POST
s это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, я не думаю, что это должно иметь значение.
2 ответа
Хорошо, я столкнулся с точно такой же проблемой, вызывая один из API-интерфейсов Microsoft Translator POST из Node.js. API работает нормально - возвращает перевод, как и ожидалось - до тех пор, пока нет символов, не являющихся ASCII, но затем, когда я добавляю один символ "é" с акцентом в соответствующий <string>
раздел тела POST, он отвечает с ошибкой:
<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
заголовок хочет длину в байтах, но я отправлял длину в символах. Почему это происходит? Ну, типичный способ измерить длину тела для http-запроса Node - это вызвать
var length = body.length
и получить "длину" - то есть количество символов - строки. Это работает, когда все символы ASCII. Однако оказывается, что в UTF-8 не-ASCII-символы (включая мой акцентированный 'é') могут содержать более одного байта каждый. Поэтому, когда тело содержит символы не ASCII, длина байта больше не будет равна длине символа, а длина символа будет неправильной. В этом случае это привело к тому, что сервер Microsoft прекратил чтение сообщения преждевременно, создав сообщение об ошибке.
Вместо этого нам нужно измерить длину в байтах при вызове (в Node.js)
var length = Buffer.byteLength(body, 'utf8')
и отправить эту длину в Content-Length
заголовок, и Microsoft Translator API снова работает.
Скорее всего, проблема не в китайском языке, а в том, что MS Translator не любит символы новой строки. Когда я наткнулся на это сообщение об ошибке, я изменил следующее:
- В каждом содержимом узла
символы новой строки заменяются пустой строкой. Эти символы имеют значения Unicode: 0xA, 0xB, 0xC, 0xD, 0x85, 0x2028, 0x2029 В каждом содержимом узла
заменены зарезервированные слова XML их альтернативным представлением: & → & amp;
<→ & lt;
> → & gt;
' → & apos;
" → & quot;
- Переставить весь XML в одну строку
После этого все работало без сбоев. Что касается вашего конкретного примера, символ "南" был переведен как "Юг". Я не использовал побег CDATA.