@thorikiriのてょりっき

@thorikiriがWebとかAndroidとかの技術ネタや本を読んだブログです

jQueryでJSON形式のリクエストが送信された場合の挙動

jQueryのPOSTでJSON形式のリクエストを送信した場合の挙動ですが、連想配列の形式でリクエストされるようです。
例えば、
$.post("url", json, function() {alert("success");}, "JSON");
としたときにJSONが、

json = {
    "data" : {
        "id" : "001",
        "name" : "thorikiri",
        "tags" : ["これはひどい", "あとでよむ"]
    }
};

であった場合のリクエストのキーと値は、

data[id] = "001"
data[name] = "thorikiri"
data[tags][] = "これはひどい", "あとでよむ"
のようになります。

sastrutsを使っている場合にも、Formにちゃんと入れて欲しいのですが、うまくいきませんでした。
何故かと言うと、連想配列だから。
data[id]は階層構造になってないとダメで、data.idである必要があるみたいです。

この時用意しているFormクラスは、

public class SampleForm {
    public SampleDto data;
}
public class SampleDto {
    public String id;
    public String name;
    public String[] tags;
}

どうにかしたいなぁと思いまして、S2RequestProcessorを拡張してみました。
連想配列の部分を階層構造のドットでつなぐようにしただけのものです。

public class JsonS2RequestProcessor extends S2RequestProcessor {
    
    private static final char NESTED_DELIM = '.';
    private static final char INDEXED_DELIM = '[';
    private static final char INDEXED_DELIM2 = ']';
    
    @SuppressWarnings("unchecked")
    @Override
    protected Map<String, Object> getAllParameters(HttpServletRequest request,
            MultipartRequestHandler multipartHandler) {
        Map<String, Object> params = new HashMap<String, Object>();
        if (request instanceof MultipartRequestWrapper) {
            request = ((MultipartRequestWrapper) request).getRequest();
        }
        @SuppressWarnings("rawtypes")
        Enumeration e = request.getParameterNames();
        while (e.hasMoreElements()) {
            String name = (String) e.nextElement();
            params.put(convert(name), request.getParameterValues(name));
        }
        if (multipartHandler != null) {
            @SuppressWarnings("rawtypes")
            Hashtable elements = multipartHandler.getAllElements();
            params.putAll(elements);
        }
        return params;
    }
    
    protected String convert(String name) {
        if (StringUtil.isEmpty(name)) {
            return name;
        }
        if (name.indexOf(INDEXED_DELIM) < 0) {
            return name;
        }
        StringBuilder sb = new StringBuilder(name.length() + 10);
        convert(sb, name);
        return sb.toString();
    }
    
    protected void convert(StringBuilder sb, String name) {
        if (StringUtil.isEmpty(name)) {
            sb.append(name);
            return;
        }
        if (name.indexOf(INDEXED_DELIM) < 0 || name.indexOf(INDEXED_DELIM2) < 0) {
            sb.append(name);
            return;
        }
        String pre = name.substring(0, name.indexOf(INDEXED_DELIM));
        String label = name.substring(name.indexOf(INDEXED_DELIM) + 1, name.indexOf(INDEXED_DELIM2));
        String next = name.substring(name.indexOf(']') + 1);
        if (StringUtil.isEmpty(label)) {
            sb.append(pre);
        } else if (StringUtil.isNumber(label)) {
            sb.append(pre).append(INDEXED_DELIM).append(label).append(INDEXED_DELIM2);
        } else {
            sb.append(pre).append(NESTED_DELIM).append(label);
        }
        convert(sb, next);
    }
}

これでちゃんと動くようになりました。
まぁ、テストしたデータは少ないので、どこに落とし穴があるかわかんないですけどね。