2014-05-09 21 views
8

Używam bibliotek klienckich Apache HTTP Client i Jacksona w moim kliencie. Kiedy zakładać JSON do serwera pojawia się błąd:Jackson-> Jackson + HttpPost = "Nieprawidłowy średni bajt UTF-8", Ustawianie mime i kodowania

org.codehaus.jackson.JsonParseException: Invalid UTF-8 middle byte 0x65 
at [Source: [email protected]; line: 1, column: 81] 

Jeśli nie określa żadnych nagłówków niż otrzymuję invalid media type, które ma sens.

Jeśli używam nagłówki skręcania i tym samym, serwer akceptuje go, więc myślę, że serwer jest OK (i tylko zbieg okoliczności, że to również za pomocą Jackson)

nich jest dokument; Mam ciężko kodowane jako Java dosłownym użyciu tylko 8 bitowe znaki w celu uniknięcia jakiegokolwiek innego miejsca do maglowania wydarzy

// "Stra\u00DFe" = "Straße" 
static String TINY_UTF8_DOC = "[{ \"id\" : \"2\", \"fields\" : { \"subject\" : [{ \"name\" : \"subject\", \"value\" : \"Stra\u00DFe\" }] } }]"; 

Oto kod używam i komentarze z różnych prób:

HttpClient httpClient = new DefaultHttpClient(); 
HttpPost post = new HttpPost(url); 

// Attempt A 
// post.setEntity( new StringEntity(content) ); 

// Attempt B 
// post.setEntity( new StringEntity(content) ); 
// post.setHeader("Content-Type", "application/json; charset=utf-8"); 

// Attempt C 
// post.setEntity( new StringEntity(content, ContentType.create("application/json")) ); 

// Attempt D 
// post.setEntity( new StringEntity(content, ContentType.create("application/json; charset=UTF-8")) ); 

// Attempt F 
// post.setEntity( new StringEntity(content, ContentType.create("application/json; charset=utf-8")) ); 

// Attempt G 
// StringEntity params = new StringEntity(content); 
// params.setContentType("application/json; charset=UTF-8"); 
// post.setEntity(params); 

// And then send to server 
HttpResponse response = httpClient.execute(post); 
int code = response.getStatusLine().getStatusCode(); 
// ...etc... 

Inne dziwne rzeczy zauważyłem:

  • przez pewien czas ten zachowywał się inaczej na Eclipse na Mac vs. prowadzenie .jar na Linux; Wyraźnie jest to symptom kodowania lub dekodowania specyficznego dla platformy, ale nie wiem gdzie. Jak na ironię, złamał po ustawieniu Eclipse, aby traktował kod jako UTF-8 (w porównaniu z ASCII). Podejrzewam, że jest to ważna wskazówka, ale nie wiem, gdzie to pasuje.
  • Widziałem czasy, gdy zamiast 2 bajtów w strumieniu są 4 bajty, chociaż może to być inny problem z kodowaniem podczas zapisywania na dysk, chociaż specjalnie ustawiłem kodowanie UTF-8 na pliku IO
  • Kiedy spójrz na element string w debugerze, widzę bajty, ale znak 8-bitowy jest liczbą ujemną. Gdy przejdziesz przez matematykę "Dwa", to wciąż jest to poprawny kodowy kod Unicode, więc nominalnie OK, zakładając, że httpclient nie jest błędny.

Naprawdę z pomysłów, i jak powiedziałem, działa z lokami, więc myślę, że serwer jest w porządku.

Edit:

curl działa podczas księgowania do serwera, ale nie mogę udostępnić kod serwera. Zwrócono uwagę, że skoro curl nie jest napisany w Javie, więc być może zachowuje się inaczej, dlatego kod serwera nadal może być podejrzany.

W ramach dalszego testu poniższy kod: NOT korzysta z biblioteki httpclient Apache i działa przy wysyłaniu wiadomości na serwer. Dowodzi to, że serwer jest w porządku i nadal jest coś nie tak z tym, jak używam biblioteki Apache po stronie klienta (lub może to błąd).

dla apache-httpclient kod, który działa:

import java.io.OutputStream; 
import java.io.OutputStreamWriter; 
import java.net.HttpURLConnection; 
import java.net.URL; 

class PostUtf8 { 
    static String POST_URL = "http://..."; 

    // \u00DF = LATIN SMALL LETTER SHARP S, looks like letter B 
    static String TINY_UTF8_DOC = "[{ \"id\" : \"2\", \"fields\" : { \"subject\" : [{ \"name\" : \"subject\", \"value\" : \"Stra\u00DFe\" }] } }]"; 

    public static void main(String [] args) throws Exception { 
     System.out.println("Posting to " + POST_URL); 
     URL url = new URL(POST_URL); 
     HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
     conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); 
     conn.setRequestMethod("POST"); 
     conn.setDoOutput(true); 
     OutputStream sout = conn.getOutputStream(); 
     OutputStreamWriter wout = new OutputStreamWriter(sout, "UTF-8"); 
     wout.write(TINY_UTF8_DOC); 
     wout.flush(); 
     int result = conn.getResponseCode(); 
     System.out.println("Result = " + result); 
    } 
} 
+1

Jest tu problem; tutaj, 'ß' jest' \ u00DF' jako sekwencja ze znakiem UTF-16, ale czytasz _byte_ 0x65; jakie jest twoje źródło? – fge

+0

Ten błąd UTF-8 zostanie wygenerowany tylko wtedy, gdy próbujesz odczytać coś jako UTF-8, który nie jest UTF-8. UTF-8 działa poprzez dopełnienie pierwszego bajtu o 1 w górnej stronie, aby wskazać liczbę bajtów do odczytania dla postaci. Reszta bajtów musi zaczynać się od "10", która działa jak bity kontrolne, inaczej wie, że parsowanie jest problemem. W twoim przypadku masz bajt, który ma 1 po lewej stronie, ale za mało bajtów, aby spełnić podany numer. Krótko mówiąc, domyślam się, że twój klient nie wysyła w ogóle UTF-8. Odsyłacz: http://en.wikipedia.org/wiki/UTF-8 – CodeChimp

+0

Czy możesz spróbować podwoić ukośnik odwrotny przed 'u00DF', aby JSON odczytał" \ u00DF "? – fge

Odpowiedz

12

Wygląda na to, że problem jest jak parametr ContentType dla StringEntity konstruktora httpclient jest tworzony.

Stała jako parametr (który odpowiada typowi mime "application/json; charset = utf-8") sprawia, że ​​wszystko działa.

Oto przykład delegowania ciąg JSON do usługi http publicznego, który odzwierciedla wniosek z powrotem do klienta:

public class HttpClientEncoding { 

    static String TINY_UTF8_DOC = "[{ \"id\" : \"2\", \"fields\" : { \"subject\" : " + 
      "[{ \"name\" : \"subject\", \"value\" : \"Stra\u00DFe\" }] } }]"; 

    public static void main(String[] args) throws IOException { 
     HttpClient httpClient = new DefaultHttpClient(); 
     HttpPost post = new HttpPost("http://httpbin.org/post"); 
     StringEntity entity = new StringEntity(TINY_UTF8_DOC, ContentType.APPLICATION_JSON); 
     //StringEntity entity = new StringEntity(TINY_UTF8_DOC, ContentType.create("application/json; charset=utf-8")); 
     post.setEntity(entity); 
     HttpResponse response = httpClient.execute(post); 
     String result = EntityUtils.toString(response.getEntity()); 
     System.out.println(result); 
     ObjectMapper mapper = new ObjectMapper(); 
     JsonNode node = mapper.readValue(result, JsonNode.class); 
     System.out.println(node.get("json").get(0).get("fields").get("subject").get(0).get("value").asText()); 
    } 
} 

wyjściowa:

{ 
    "origin": "46.9.77.167", 
    "url": "http://httpbin.org/post", 
    "args": {}, 
    "data": "[{ \"id\" : \"2\", \"fields\" : { \"subject\" : [{ \"name\" : \"subject\", \"value\" : \"Stra\u00dfe\" }] } }]", 
    "files": {}, 
    "form": {}, 
    "headers": { 
    "Content-Length": "90", 
    "User-Agent": "Apache-HttpClient/4.3.3 (java 1.5)", 
    "Host": "httpbin.org", 
    "Connection": "close", 
    "X-Request-Id": "c02864cc-a1d6-434c-9cff-1f6187ceb080", 
    "Content-Type": "application/json; charset=UTF-8" 
    }, 
    "json": [ 
    { 
     "id": "2", 
     "fields": { 
     "subject": [ 
      { 
      "value": "Stra\u00dfe", 
      "name": "subject" 
      } 
     ] 
     } 
    } 
    ] 
} 
Straße 
+2

Dziękuję, to naprawiło. Wygląda na to, że ciągi-kontra-ciągi są w nowszych wersjach, ale przykłady, które znalazłem online były starsze. Dziękuję również za link do strony httpbin.org i przykład przeczytania odpowiedzi, ponieważ jestem nowy w tej bibliotece. –

+0

Próbowałem utworzyć StringEntity za pomocą 'ContentType.APPLICATION_JSON' 2nd arg ... lub dodać nagłówek do obiektu HttpPost, np. Request.addHeader (" Content-Type ", ContentType.APPLICATION_JSON.toString()); '. Ale ciągle pojawia się ten sam błąd "JsonMappingException: nieprawidłowy środkowy bajt UTF-8 0xZZ", gdy moja wiadomość zawiera na przykład "é", "è" lub "ô" ... ZZ jest wartością szesnastkową, która zmienia się w zależności od wartości. – Alex