改行コードを含む JSON を返す Servlet
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { servletResponse.setContentType("application/json"); servletResponse.setCharacterEncoding("UTF-8"); PrintWriter writer = servletResponse.getWriter(); writer.write("{\"str\" : \"HO\nGE\"}"); // !!! writer.flush(); writer.close(); }
文字列「HOGE」のど真ん中に改行コードを含む、 JSON 的によろしくない JSON を返す Servlet を作ります。
android クライアント側
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onResume() { super.onResume(); new Thread(new Runnable() { @Override public void run() { HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet("http://192.168.0.5:8080/json"); HttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet); } catch(ClientProtocolException e) { Log.e("AndroidClient", "ClientProtocolException! " + e.getMessage()); } catch(IOException e) { Log.e("AndroidClient", "IOException! " + e.getMessage()); } if(httpResponse != null) { try { JSONObject json = new JSONObject(EntityUtils.toString(httpResponse.getEntity())); String str = json.getString("str"); Log.d("AndroidClient", "str : " + str); } catch(ParseException e) { Log.e("AndroidClient", "ParseException! " + e.getMessage()); } catch(JSONException e) { Log.e("AndroidClient", "JSONException! " + e.getMessage()); } catch(IOException e) { Log.e("AndroidClient", "IOException! " + e.getMessage()); } } } }).start(); }
んで、 Servlet にリクエスト飛ばしてレスポンスを org.json.JSONObject に変換するような android クライアントアプリを作ります。
それを eclair(2.1-update1) エミュレーター上で動かす
JSONException! Unterminated string at character 13 of {"str" : "HO GE"}
「Unterminated string at character 13 of ...」と言われて落ちます。「android クライアント側」の 23 行目で落ちて、 29 行目のエラーログが吐かれます。
今度は froyo(2.2) エミュレーター上で動かす
str : HO GE
あら不思議。 25 行目のログが出力されちゃうじゃありませんの!
eclair と froyo のソースを見比べる
「dalvik/libcore/json/src/main/java/org/json」以下です。
eclipse 上で「 compare with 」とかしてもあまり意味ありません。インターフェースというか public なメソッドのシグニチャーだけ同じで実装は随分違っている雰囲気です。
JSONObject に食わせた String をパースする org.json.JSONTokener を見比べてみると・・・
public class JSONTokener { // (中略) public String nextString(char quote) throws JSONException { char c; StringBuilder sb = new StringBuilder(); for (;;) { c = next(); switch (c) { case 0: case '\n': case '\r': throw syntaxError("Unterminated string");
上は eclair
下が froyo (private な深いメソッドまで行ってますが、まあこの辺だと思います)
public class JSONTokener { // (中略) private int nextCleanInternal() throws JSONException { while (pos < in.length()) { int c = in.charAt(pos++); switch (c) { case '\t': case ' ': case '\n': case '\r': continue;
スルーとスローは随分違うと思いますが、 JSON を返す側(ここでは Servlet 側)がちゃんとエスケープすれば、どちらもなんの問題も起きません。
0 件のコメント:
コメントを投稿