2013年7月25日木曜日

[Java]数値のラッパー型をintに変換する

引数で何らかの数値ラッパー型がObjectで引き渡される。それをintとして利用したい的なことありませんか? ありませんね。
でも、汎用的な変換ライブラリなどではそんなことあるかもしれませんね。 instanceofを全部の数値ラッパー型に適応して。。。涙が出そうになります。ということで調べてみるとNumberというインタフェースがあり、これが使えることがわかりました。 IntegerやDoubleのリファレンスを見るとわかるとおり、ラッパー型のクラスはみんなNumberを継承しているんです。 コードはこんな感じ。

public static void main(String[] args) {
	Object d = Double.valueOf(3.5d);
	System.out.println("3.5d=" + toInt(d));
 
	Object f = Float.valueOf(2.5f);
	System.out.println("2.5f=" + toInt(f));
 
	Object l = Long.valueOf(20l);
	System.out.println("20l=" + toInt(l));
 
	Object b = Byte.valueOf((byte) 20);
	System.out.println("20b=" + toInt(b));
 
	Object s = Short.valueOf((short) 20);
	System.out.println("20s=" + toInt(s));
 
	Object bd = new BigDecimal(4.5);
	System.out.println("4.5bd=" + toInt(bd));
 
	Object al = new AtomicLong(21);
	System.out.println("21al=" + toInt(al));
 }
 
/**
 * int変換.
 * 
 * 数値ラッパーをintに変換する。
 * 
 * @param obj
 * @return 変換した数値
 */
public static int toInt(Object obj) {
	if (!(obj instanceof Number)) {
		throw new IllegalArgumentException("not number!!");
	}
	return ((Number) obj).intValue();
}
実行結果はこんな感じ。

3.5d=3
2.5f=2
20l=20
20b=20
20s=20
4.5bd=4
21al=21

2013年7月23日火曜日

[Java]JSONをPOJOにバインドするとき、オリジナルの変換を行う

JSONからPOJO(Bean)に変換する処理はめんどくさいです。たとえば、
  • JSONは文字列で、Java側はEnum
このケースでは、文字列=Enumの定義名という式が成り立てば、自動で変換されます。下のstep1がそれに該当します。しかし文字列がenumと一致しない場合、正しく変換されません。おそらく初期値になってしまうでしょう。以下の例ではnullになってしまっています(enumはnullを許容するんです)。
次に、
  • JSON上は数値なんだけど、Java上はEnum
この場合、自動的な変換では明らかに無理です。こういう場合、JsonDeserializer<T>を使います。 以下の例では、数値をenumにしています。こちらも存在しない値の場合、nullを返却しています。 実装した例はこんな感じ。

public class GsonEnum {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // タイプを取り出す(List)
        Type type = new TypeToken<List<Signal>>() {
        }.getType();

        // JSON->List<Signal>
        List<Signal> signal = gson.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("signal2.json")), type);

        System.out.println("---step1---");
        System.out.println(signal);

        // JSON->List<Signal>
        // #1 TypeAdapterを設定したGsonを生成する
        Gson gson2 = new GsonBuilder().registerTypeAdapter(SignalStat.class,
                new SignalStatDeserializer()).create();
        List<Signal> signal2 = gson2.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("signal3.json")), type);

        System.out.println("---step2---");
        System.out.println(signal2);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class Signal {
        private SignalStat stat;
        private String message;
        private SignalStat next;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("stat=").append(stat);
            sb.append(" message=").append(message);
            sb.append(" next=").append(next);
            sb.append("]");

            return new String(sb);
        }
    }

    /**
     * ステータスの表現.
     */
    enum SignalStat {
        Red, Yellow, Blue;

        // #2 変換用のMAP(int -> SignalStat)
        static Map<Integer, SignalStat> CONVERTER = new HashMap<Integer, SignalStat>();

        static {
            for (SignalStat stat : SignalStat.values()) {
                CONVERTER.put(stat.ordinal(), stat);
            }
        }
    }

    /**
     * SignalStat用Deserializer.
     */
    static private class SignalStatDeserializer implements
            JsonDeserializer<SignalStat> {

        @Override
        public SignalStat deserialize(JsonElement json, Type typeOfT,
                                      JsonDeserializationContext context) throws JsonParseException {
            // #3 int -> SignalStat
            return SignalStat.CONVERTER.get(json.getAsInt());
        }
    }
}
ポイントは以下の通り
  • #1:TypeAdapterを設定したGsonを生成するためにGsonBuilderをnewしregisterTypeAdapterでDeserializerを指定する
  • #2:enumの中に、変換用Mapを用意しておく。staticでOKです
  • #3:JsonDeserializerを実装する。#2の変換用Mapを使ってint->enumを実現している
入力データはこんな感じ(signal2.json)

[
    {
        "stat": "Red",
        "message": "とまれ",
        "next": "Blue"
    },
    {
        "stat": "Yellow",
        "message": "とまっとけ",
        "next": "Red"
    },
    {
        "stat": "Invalid",
        "message": "すすんでもいいよ",
        "next": "Yellow"
    }
]
入力ry (signal3.json)

[
    {
        "stat": 0,
        "message": "とまれ",
        "next": 2
    },
    {
        "stat": 1,
        "message": "とまっとけ",
        "next": 0
    },
    {
        "stat": 100,
        "message": "すすんでもいいよ",
        "next": 1
    }
]
出力結果はこんな感じ
step1ではInvalidは存在しないのでnullとなり、step2では100は存在しないのでnullになっています。

---step1---
[[stat=Red message=とまれ next=Blue], [stat=Yellow message=とまっとけ next=Red], [stat=null message=すすんでもいいよ next=Yellow]]
---step2---
[[stat=Red message=とまれ next=Blue], [stat=Yellow message=とまっとけ next=Red], [stat=null message=すすんでもいいよ next=Yellow]]

2013年7月22日月曜日

[Android]透明なアクティビティ

テーマ設定で透明なアクティビティが作れます。えぇまぁ AndroidManifest.xmlに書くだけです。

<activity android:name="com.hoge.TransparentActivity"
 android:theme="@android:style/Theme.Translucent"
 android:label="Transparent"/>

2013年7月21日日曜日

[Java]JSONをJsonObject/JsonArrayにバインドする

JSONからPOJO(Bean)に変換するんだけど、一部分だけJsonObjectに落としたい。そんな時はありませんか?
どんなデータが入ってくるのか決まっていない。けどキーは同じみたいな。結果的に汎用型で受け付けたい。そんなケースです。
いろいろ調べたんだけど方法があわからず。でしたが、実は普通にJsonObjectやJsonArrayつかえば良い。ということがわかりました。 配列の場合だけ気を付けないとダメ。という残念なことになっています。
コードはこんな感じ。


public class GsonObject {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // JSON->JsonObject(Map)
        JsonObject his = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history_map.json")), JsonObject.class);

        System.out.println("---step1 Map---");
        System.out.println(his);

        // JSON->JsonArray(List)JsonArrayじゃないとキャストに失敗する。。。
        JsonArray his2 = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history_list.json")), JsonArray.class);

        System.out.println("---step2 List---");
        System.out.println(his2);

        // JSON->JsonObject(POJO)
        JsonObject his3 = gson.fromJson(new InputStreamReader(GsonList.class
                .getResourceAsStream("history.json")), JsonObject.class);
        System.out.println("---step3 Object---");
        System.out.println(his3);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
出力結果はこんな感じ。

---step1 Map---
{"kentousi":{"key":894,"message":"遣唐使"},"kamakura1":{"key":1192,"message":"鎌倉幕府"},"kamakura2":{"key":1185,"message":"鎌倉幕府"}}
---step2 List---
[{"key":894,"message":"遣唐使"},{"key":1192,"message":"鎌倉幕府"},{"key":1185,"message":"鎌倉幕府"}]
---step3 Object---
{"key":894,"message":"遣唐使"}
インプットしたデータは省略。

[Java]JSONをPOJOにバインドする(List,[])

JSONからPOJO(Bean)に変換する処理はめんどくさいです。 Google社製のGSONでは、データ形式にListや配列としてバインドすることができます。 で、コードはこんな感じ。

class GsonList {

    public static void main(String[] args) {
        Gson gson = new Gson();
        // List.classと書けないので、TypeTokenを使ってTypeを取り出す
        Type type = new TypeToken>() {}.getType();

        // JSON->List
        List his = gson.fromJson(new InputStreamReader(
                GsonList.class.getResourceAsStream("history_list.json")), type);

        System.out.println("---step1 List---");
        System.out.println(his);

        // JSON->POJO[]
        History[] his2 = gson.fromJson(new InputStreamReader(
                GsonList.class.getResourceAsStream("history_list.json")), History[].class);

        System.out.println("---step2 []---");
        System.out.println(Arrays.toString(his2));
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
出力結果は次の通り。

---step1 List---
[[key=894 message=遣唐使], [key=1192 message=鎌倉幕府], [key=1185 message=鎌倉幕府]]
---step2 []---
[[key=894 message=遣唐使], [key=1192 message=鎌倉幕府], [key=1185 message=鎌倉幕府]]
インプットしたJSONは次の通り。

[
    {
        "key": 894,
        "message": "遣唐使"
    },
    {
        "key": 1192,
        "message": "鎌倉幕府"
    },
    {
        "key": 1185,
        "message": "鎌倉幕府"
    }
]

[Java]JSONをPOJOにバインドする(Map)

JSONからPOJO(Bean)に変換する処理はめんどくさいです。
Google社製のGSONでは、データ形式にMapとしてバインドすることができます。 残念なことに、Map形式では順序を維持できないという課題があります。
しかしこれはJSONの仕様であり、GSONとしてはその仕様に準じているだけ。といえますね。 

コードはこんな感じ。

public class GsonMap {

    public static void main(String[] args) {
        Gson gson = new Gson();
        // Map.classと書けないので、TypeTokenを使ってTypeを取り出す
        Type type = new TypeToken>() {
        }.getType();

        // JSON->Map
        Map his = gson.fromJson(new InputStreamReader(
                GsonMap.class.getResourceAsStream("history_map.json")), type);

        System.out.println(his);
    }

    /**
     * 解析結果を受け取るクラス
     */
    static class History {
        private int key;
        private String message;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("[");
            sb.append("key=").append(key);
            sb.append(" message=").append(message);
            sb.append("]");

            return new String(sb);
        }
    }
}
実行結果はこんな感じ

{
    "kentousi"=["key"=894 "message"="遣唐使"],
    "kamakura1"=["key"=1192 "message"="鎌倉幕府"],
    "kamakura2"=["key"=1185 "message"="鎌倉幕府"]
}
インプットに使ったJSONはこんな感じ

{
    "kentousi": {
        "key": 894,
        "message": "遣唐使"
    },
    "kamakura1": {
        "key": 1192,
        "message": "鎌倉幕府"
    },
    "kamakura2": {
        "key": 1185,
        "message": "鎌倉幕府"
    }
}

[Java]JSONをPOJOにバインドする

JSONからPOJO(Bean)に変換する処理はめんどくさいです。リフレクションでルールを決めて変換。というのもめんどくさいですね。 そこで、JSONを解析するライブラリを利用します。 JSON系のライブラリはいくつかありますが、今回はGoogle社製のGSONを使ってみました。コードはこんな感じ。 よく紹介されているレベルのコードと変わりませんが、以下のようなポイントがあります。
  • private  static final の変数に値を突っ込んでいる
  • 内部クラス(staticですが)を利用する
  • 変数名と異なる項目をバインドしている
この辺を気にする人には参考になると思います。

public class GsonBasic {

    public static void main(String[] args) {
        Gson gson = new Gson();

        //JSON->POJO(1)
        History his = gson.fromJson(
                new InputStreamReader(GsonBasic.class
                        .getResourceAsStream("history.json")), History.class);
        System.out.println("---step1---");
        System.out.println(his);

        //JSON->POJO(1)
        History2 his2 = gson.fromJson(
                new InputStreamReader(GsonBasic.class
                        .getResourceAsStream("history.json")), History2.class);
        System.out.println("---step2---");
        System.out.println(his2);
    }

    // 内部クラスにする場合、staticにしないと面倒が起る

    /**
     * 解析結果を受け取るクラス(1)
     */
    static class History {
        // final にすることもできる
        private final int key;
        private final String message;

        // コンストラクタで初期化することで、GSONでもセットできる
        public History() {
            key = 0;
            message = null;
        }

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("key=").append(key);
            sb.append(" message=").append(message);

            return new String(sb);
        }
    }

    /**
     * 解析結果を受け取るクラス(2)
     */
    static class History2 {
        // JSONのキー名と変数名が異なる場合、@SerializedNameを使う
        @SerializedName("key")
        private int mKey = 0;
        @SerializedName("message")
        private String mMessage = "";

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append("key=").append(mKey);
            sb.append(" message=").append(mMessage);

            return new String(sb);
        }
    }
}
出力結果は次の通り。

---step1---
key=894 message=遣唐使
---step2---
key=894 message=遣唐使
インプットしたJSONは次の通り。

{
    "key": 894,
    "message": "遣唐使"
}

2013年7月6日土曜日

[.NET]Dictionaryを素直?にJSONで出力する

.NET系でJSONを出力する場合、DataContractJsonSerializerというクラスがあり、これを使えばそれっぽく出力されます。
しかし、Dictionaryで持っている値を出力すると、なんかkeyとvalueの配列という残念な形式にシリアライズされてしまいます。
他にいい方法ないかなぁと思って調査してみたところ、.NET4.0以降では、もう一種類JSONのシリアライザがあるのがわかり、
試したところ、期待するような形式で出てきました。ということでコードはこんな感じ。


private void button1_Click(object sender, EventArgs e)
{
    // using System.Web.Script.Serialization;
    var json = new JsonData();
    json.item_key = "歴史";

    json.label_position["kentousi"] = new { key = 894, Message = "遣唐使" };
    json.label_position["kamakura1"] = new { key = 1192, Message = "鎌倉幕府" };
    json.label_position["kamakura2"] = new { key = 1185, Message = "鎌倉幕府" };
    json.label_position["gomi"] = new { key = "string", Message = new string[]{"array1","array2"} };

    var sel = new JavaScriptSerializer();
    System.Diagnostics.Debug.WriteLine(sel.Serialize(json));

}

class JsonData
{
    public string item_key;
    public Dictionary<string, Object> label_position = new Dictionary<string, Object>();
}
出力結果はこんな感じ。

{
    "item_key": "歴史",
    "label_position": {
        "kentousi": {
            "key": 894,
            "Message": "遣唐使"
        },
        "kamakura1": {
            "key": 1192,
            "Message": "鎌倉幕府"
        },
        "kamakura2": {
            "key": 1185,
            "Message": "鎌倉幕府"
        },
        "gomi": {
            "key": "string",
            "Message": [
                "array1",
                "array2"
            ]
        }
    }
}


DataContractJsonSerializerでは、匿名型をサポートしていないのに対して、JavaScriptSerializerはイケる。という特徴もありました。ということで、組み合わせの異なる型をガラガラとシリアライズする必要のあるサーバ機能的な物を作る場合、というか4以降ならJavaScriptSerializerがおすすめ。

2013年7月3日水曜日

[Android]ADB server didn't ACKがでて端末がつながらない

最近、Nexus7がつながっているのにもかかわらず、EclipseのDeviceに実機が出てこないことがありました。 で、仕方がないのでADBを再起動するために、コマンドラインでADBを再起動します。 コマンドはこんな感じ。


D:\>adb kill-server

D:\>adb start-server
* daemon not running. starting it now on port 5037 *
ADB server didn't ACK
* failed to start daemon *

という感じで、ADBが再起動できません。何度がkill-serverするとイイみたいなことが書いてあるサイトがありましたが、私の環境では効果がありませんでした。
こういう場合、Windowsの場合タスクマネージャーにadbのプロセスがいるかをチェックします。いる場合、プロセスを停止した後、adb start-serverすると復帰します。
私の環境では、これで回復します。

D:\>adb kill-server

D:\>adb start-server
* daemon not running. starting it now on port 5037 *
* daemon started successfully *



この方法に気が付くまでは、マシン再起動していました。。。。

たぶん、こうなっちゃうのは、Nexus7のUSB端子の接触不良のような気がします。肘などで意図せずその辺を触ってしまうと起こる場合があります。