POSTing XML с китайскими символами в Microsoft Translator API вызывает исключение десериализации

Я пытаюсь перевести с китайского (упрощенного) на английский с помощью Microsoft Translator API.

Пара требований

  • Я должен использовать HTTP-метод POST, а не GET со строкой запроса, потому что мои запросы превышают лимит URI Microsoft в 15 845 символов (обратите внимание, что это возможно, даже если я использую менее 10 000 символов в случае китайских символов. Причина заключается в том, что строка запроса должна быть закодирована URL-адресом, что значительно увеличивает длину, но она декодируется Microsoft до определения количества символов.

  • Единственный метод перевода HTTP, который позволяет POSTs, - это TranslateArrayMethod, например TranslateMethod разрешает только GETs. К сожалению, 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, я не думаю, что это должно иметь значение.


person Tom    schedule 15.07.2015    source источник
comment
Как вы конструируете XML - используете ли вы библиотеку / API или выполняете искажение строк? Если последнее, соответствует ли кодировка, объявленная в объявлении <?xml ?>, фактической кодировке, которую вы используете?   -  person Damien_The_Unbeliever    schedule 15.07.2015
comment
@Damien_The_Unbeliever Я занимаюсь искажением строк. Я не добавляю никаких объявлений xml, потому что это привело к пустому ответу от API. Я снова попытался добавить <?xml version="1.0" encoding="ISO-8859-1"?> в начало, и он вернул пустой ответ с кодом состояния 400 и заголовками {"content-length":"0","date":"Wed, 15 Jul 2015 08:41:54 GMT","connection":"close"}. Фактически, я использую node.js, и событие ответа data никогда не запускается, поэтому сервер никогда не отправляет никаких данных - он закрывает соединение без какого-либо ответа, кроме кода состояния 400.   -  person Tom    schedule 15.07.2015
comment
@Damien_The_Unbeliever обратите внимание, что Microsoft также не добавляет этот тег объявления xml в свой ввод XML, см. msdn.microsoft.com/en-us/library/ff512422.aspx   -  person Tom    schedule 15.07.2015


Ответы (2)


Хорошо, я столкнулся с той же проблемой при вызове одного из API POST переводчика Microsoft из 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 снова заработает.

person Teg Grenager    schedule 13.05.2016
comment
Обратите внимание, что не все буквы с диакритическими знаками занимают 2 байта в UTF-8. Некоторые, например ẘ, занимают 3 байта. - person Mr Lister; 15.05.2016

Скорее всего, проблема не в китайском языке, а в том, что MS Translator не любит символы новой строки. Когда я наткнулся на это сообщение об ошибке, я изменил следующее:

  1. В каждом содержимом узла ‹string> заменены символы новой строки пустой строкой. Эти символы имеют значения Unicode: 0xA, 0xB, 0xC, 0xD, 0x85, 0x2028, 0x2029.
  2. В каждом содержимом узла ‹string> заменены зарезервированные слова XML на их альтернативное представление:

    & →

    ‹→

    ›→

    '→

    "→

  3. Переставил весь XML в одну строку

После этого все наладилось. Что касается вашего конкретного примера, символ «南» был переведен как «Юг». Я не использовал экранирование CDATA.

person Zagavarr    schedule 21.11.2015