java 板


LINE

blog 版:http://blog.dontcareabout.us/2013/12/gwt-autobean.html BBS 版以 markdown 语法撰写 ______________________________________________________________________ [AutoBean] 是目前打算拿来在 GWT 中处理 JSON 的工具。 不过在讲正事之前,先扯两段杂谈 [殴飞] [AutoBean]: https://code.google.com/p/google-web-toolkit/wiki/AutoBean ## 杂谈 1:这样也可以? ## 要不是要弄 web API,其实也不会想碰 JSON, GWT RPC 好好的干麽弄什麽 JSON [远目]。 不过 server side 要处理 JSON 其实有 [GSON], 正常 encode / decode 真的都还蛮简单的, 简单到不知道能介绍什麽 XD。 是说 LaPass(ptt.cc)因为有一个我觉得有点诡异的需求, 结果挖出了 [TypeAdapterFactory 的用法], 看起来真的很乾净很炫(炫到都快看不懂了 [遮脸]), 只能说好 library 如当是也。 理所当然的,会想看看有没有 GWT 版的 GSON, 结果看到 [bGwtGson] 这玩意差点喷出来。 因为他的作法是用 GWT RPC 把东西丢到 server side, 这样 server side 就有 [GSON] 可以用了... 揪咪... 我都不知道该说牛逼还是坑爹,这世界果然很广大阿 [握拳] 喔对,顺带一提,[GSON] 在 AppEngine 上也可以使用。 [GSON]: https://code.google.com/p/google-gson/ [TypeAdapterFactory 的用法]: #1IVxn9h0 (java) [bGwtGson]: https://github.com/heroandtn3/bGwtGson ## 杂谈 2:谜样里技? ## 我搞不太懂 [AutoBean] 在 GWT 当中扮演什麽样的角色? 彷佛还蛮多人在用的(因为大家都炸同样的问题... Orz), 但是官方指南似乎没有这个东西(JavaDoc 当然还是有), 教学文件只有出现在 google code 的 wiki 上, 所以这是里技吗? 我比较怕这是即将被抛弃的里技 Orz 毕竟要在 GWT 里头处理 JSON 并不是太麻烦。 官方指南建议的 JSNI / Overlay Types 写起来堪称简单直觉, 尤其是跟 [AutoBean] 相比的话 [死]。 再不然 [GWT-Jackson] 好像也是种选择? 刚好两者的风格我都不爱 [泪目] 最大的哏在於 [AutoBean] 并没有办法直接处理 `List<T>` 这种东西, [这个 bug][Issue 6904] 在 2011.10 被提出之後, 2012.11 最後一个 comment 之後就无声无息了, 目前最新的 GWT 2.5.1 还是有同样的问题 Zzzz,只能靠 workaround。 後头会详述这些事情 [死]。 [GWT-Jackson]: https://github.com/nmorel/gwt-jackson [Issue 6904]: https://code.google.com/p/google-web-toolkit/issues/ detail?id=6904 ## 怎麽用? ## 好了,终於要进入正题了。 [握拳] GWT 已经内建 [AutoBean],所以不用另外挂 jar 档, 但是要在 `gwt.xml` 当中补上: <inherits name="com.google.web.bindery.autobean.AutoBean"/> 如果你要把下面这个 JSON 字串转成 `Foo` 的 instance { "uid":"cde6c847-d072-4d33-82bd-93fa4710dc9b", "limit":50, "deleted":true, "update":1388157386000 } 首先... `Foo` 得是个 bean 的 interface,定义一堆 getter/setter, 名称得跟 JSON 里头的一致: interface Foo { public void setUid(String uid); public void setLimit(int limit); public void setDeleted(boolean deleted); public void setUpdate(Date update); public String getUid(); public int getLimit(); public boolean isDeleted(); public Date getUpdate(); } 转换的时候需要先建立一个 factory,通常会这样写 interface MyFactory extends AutoBeanFactory { AutoBean<Foo> foo(); } MyFactory factory = GWT.create(MyFactory.class); 然後... 终於可以 decode 了: String foo = "{" + "\"uid\":\"cde6c847-d072-4d33-82bd-93fa4710dc9b\"," + "\"limit\":50," + "\"deleted\":true," + "\"update\":1388157380000" + "}"; Foo instance = AutoBeanCodex.decode(factory, Foo.class, foo).as(); encode 的话就是: String fooJson = AutoBeanCodex.encode( AutoBeanUtils.getAutoBean(instance) ).getPayload(); 如果愿意在 MyFactory 里头加一个 method: interface MyFactory extends AutoBeanFactory { AutoBean<Foo> foo(); //下面这个是新增的 AutoBean<Foo> genFooBean(Foo foo); } 那不用 `AutoBeanUtils.getAutoBean()` 而是 String fooJson = AutoBeanCodex.encode( factory.genFooBean(instance) ).getPayload(); 有没有比较快乐就见仁见智,不过後面会需要用到後面这招, 或着这麽说比较实在:「请忘记 `AutoBeanUtils` 吧」。 ### 注意事项 ### 如果到这边你还能忍受 [AutoBean], 那先讲几个我已经炸到,但可以理解的哏, 主要是跟 [GSON] 的差异。 1. `Gson.toJson()` 遇到 false / null 值不会省略该 field, 但是 AutoBean 会。 也就是说,如果 `foo.setDeleted(false);`, 那麽 `AutoBeanCodex.encode()` 出来的字串不会看到 `deleted`。 当然,这其实不妨碍正常运作。 [GSON] 的作法可能有些人还会觉得怪? 2. 日期(`java.util.Date`)的处理。 `Gson.toJson()` 会用 `Date.toString()` 作值(反之亦然), 但是 [AutoBean] 则是用 `Date.getTime()`(反之亦然)。 只能说还是统统用 long 表示日期就算了 (然後在 JSON 当中最好还把这数字当成字串, 免得像 32bit 的 PHP 还给你耍花招 [怨念ing])。 至於 [Joda] 要解决的议题... 遇到再说 XD (*有遇到会再补上来 Orz*) [Joda]: http://www.joda.org/joda-time/ ## `List` 的炸点 ## 如果你永远不会 decode / encode 一个 array 或是 `List`, 那恭喜你,除了写法稍稍扭曲一点之外, [AutoBean] 是可以接受、也算好用的(应该啦...)。 实际上... 别闹了,怎麽可能不处理 `List`? 於是 [AutoBean] 就成了茶几──上头摆满了悲剧。 ### decode ### 以 [GSON] 吐出来的东西来看,一个 `List<Foo>` 的 instance 会长这样 (喔对,我把 `update` 的型态改成 long 了 XD): [ {"uid":"cde6c847-d072-4d33-82bd-93fa4710dc9b", "limit":50,"deleted":false,"update":"1388157380000"}, {"uid":"a391dedf-1f81-4380-a712-59eac4d9aea3", "limit":50,"deleted":false,"update":"1388157380000"} ] 想依样画葫芦比照办理时... 等等,`AutoBeanCodex.decode()` 的第二个参数要给什麽? 然後於是有人弄出了这个 [workaround][decode workaround]。 首先,要建一个 interface 来代表 `List<Foo>` 这玩意: interface FooList { public void setList(List<Foo> list); public List<Foo> getList(); } factory 的 interface 则是: interface MyFactory extends AutoBeanFactory { //下面这个暂时用不到 AutoBean<Foo> genFooBean(Foo foo); AutoBean<FooList> fooList(); } 最後,要对拿到的 JSON 字串动手脚,变成这样: FooList fooList = AutoBeanCodex.decode( factory, FooList.class, "{\"list\":" + foo + "}" ).as(); List<Foo> instance = fooList.getList(); 简单地说,就是 Java 的部份你要让他有个 class 为依归, 但是光这样还不够,因为 [AutoBean] 不知道要从何处理起, 所以 JSON 的部份你也要伪造一下...... 等等,还没完,好戏压箱底、好酒沈瓮底, encode 的部份那才叫经典。 [decode workaround]: http://stackoverflow.com/questions/13651068/ gwt-autobean-how-to-handle-lists ### encode ### 要把一个 `List<Foo>` 转成 JSON,这到底是有多难? 不难,如果把刚刚 `AutoBeanCode.decode()` 出来的 `fooList` 再次转成 JSON, 那麽只要 factory 加上 interface MyFactory extends AutoBeanFactory { //下面这个暂时用不到 AutoBean<Foo> genFooBean(Foo foo); AutoBean<FooList> fooList(); //下面这个是新增的 AutoBean<FooList> genFooListBean(FooList instance); } 立马就转,没有问题!(也完全没意义 ==") AutoBeanCodex.encode( factory.genFooListBean(fooList) ).getPayload(); 如果是把既有的 `List<Foo>` instance 转换, 依照上面的逻辑,得先实做那个毫无意义的 `FooList`: FooList fooList = new FooList() { List<Foo> list = new ArrayList<Foo>(); @Override public void setList(List<Foo> list) { this.list = list; } @Override public List<Foo> getList() { return list; } }; //FooImpl 就容许我跳过,反正就是 implements Foo 的东东 fooList.getList().add(new FooImpl()); AutoBeanCodex.encode( factory.genFooListBean(fooList) ).getPayload(); 执行上面这段程式码,你就会发现 `AutoBeanCodex.encode()` 快乐的炸了 NPE,而且完全搞不懂发生了什麽事情。 这是个已知的 bug([Issue 6904]), 虽然不知道会不会有人去解...... Orz。 而世界还真的是很广大,有人也找出了 [workaround][encode workaround], 解法就是你不能直接丢 `FooImpl` 的 instance, 得用 `MyFactory.genFooBean()` 产生出 `AutoBean<Foo>`, 再藉由它(`as()`)取得 `Foo` 的 instance 才可以...... 写的我自己都乱了,看 code 比较实在: interface MyFactory extends AutoBeanFactory { AutoBean<Foo> genFooBean(Foo foo); AutoBean<FooList> fooList(); AutoBean<FooList> genFooListBean(FooList instance); } fooList.getList().add( //原本是 new FooImpl() factory.genFooBean(new FooImpl()).as() ); AutoBeanCodex.encode( factory.genFooListBean(fooList) ).getPayload(); 套最近流行的句型: 「如果这不叫脱裤子放屁,我还 ____ 的不知道什麽才叫脱裤子放屁」 喔对,无论 `genFooBean()` 还是 `genFooListBean()` 都不能用官方文件用的 `AutoBeanUtils.getAutoBean()`。 如果拿他替换 `genFooBean()`,一样喷 NPE; 如果拿它替话 `genFooListBean()`,不会喷 NPE, 而是转换出来的 JSON 字串会是 null。 WTF [encode workaround]: https://groups.google.com/d/msg/google-web-toolkit/nvIotNHy-Io/GkXz_WQXvR4J ## 结尾 murmur ## 拿 [GSON] 跟 [AutoBean] 相比是很有趣的。 一个是完美到不需要了解内部到底发生什麽事情, 一个则是太糟糕了,所以根本不想了解。 短时间之内,我可能还是不会放弃 [AutoBean], 除非 GWT 2.6 就遗弃 [AutoBean], 或是找到更好的 tool(而不是 [GWT-Jackson] 那种 style)。 都花了这麽多时间了,就看看能<strike>被炸到</strike>走到什麽程度。 写到後来,都不知道到底是在介绍推广还是在吐槽。 只能说,嗯... 我对 GWT 真的很有爱...... [远目] -- 钱锺书: 说出来的话 http://www.psmonkey.org 比不上不说出来的话 Java 版 cookcomic 版 只影射着说不出来的话 and more...... --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 59.115.237.204







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Soft_Job站内搜寻

TOP