Ajax 的 Java 对象序列化

80酷酷网    80kuku.com

  ajax|对象

如果您正在使用异步 JavaScript 和 XML(Ajax)进行 Java™ Web 开发,那么您最关心的问题可能就是把数据从服务器传递给客户机。在面向 Java 开发人员的 Ajax 系列的文章中,Philip McCarthy 介绍了 Java 对象序列化的五种方式,并提供了选择最适合应用程序的数据格式和技术所需要的全部信息。本文将侧重于许多 Java Web 开发人员最关心的问题:为客户机生成数据。

多数 Java 开发人员已经把模型-视图-控制器(MVC)模式应用在他们的 Web 应用程序上。在传统的 Web 应用程序中,视图组件由JSP 或者其他表示技术(例如 Velocity 模板)构成。这些表示组件动态地生成全新的 HTML 页面,替代用户以前正在查看的页面,从而更新用户界面。但是,在 Java Web 应用程序使用 Ajax UI 的情况下,基于从 XMLHttpRequest 的响应接收到的数据,JavaScript 客户端代码对于更新用户看到的内容负有最终责任。从服务器的角度来看,视图成为它响应客户机请求而发送的数据表示。

这篇文章侧重于可以用来生成 Java 对象以数据为中心的视图的技术。我将演示可以把 JavaBeans 变成 XML 文档的各种方法,并且讨论每种方法的优劣。您将看到为什么 XML 并不总是最好的途径:对于简单的 Ajax 请求来说,传输纯文本更好。最后,我将介绍 JavaScript 对象标注(JSON)。JSON 允许数据以序列化的 JavaScript 对象图的形式传输,在客户端代码中处理序列化的 JavaScript 对象图极为容易。

关于示例

我将使用一个示例应用程序和几个用例来演示这里讨论的技术特性和技术。图 1 显示的极为简单的数据模型可以表示示例用例。这个模型代表在线商店中的顾客帐户。顾客拥有以前订单的集合,每个订单包含几个商品。

图 1. 简单的对象模型

虽然 XMLHttpRequest 对于发送数据使用的格式没有做任何限制,但是对于多数目的来说,只发送传统的表单数据是适合的,所以我的讨论集中在服务器的响应上。响应也可以有基于文本的格式,但是正如它的名字表示的,XMLHttpRequest 具有内置的处理 XML 响应数据的能力。这使 XML 成为 Ajax 响应的默认选择,所以我们从 XML 格式开始讨论。

从 Java 类产生 XML

把 Ajax 响应作为 XML 来传递有许多原因:每个支持 Ajax 的浏览器都有导航 XML 文档的方法,也有许多服务器端技术可以处理 XML 数据。通过制定一个方案,描述要交换的文档类型,在 Ajax 客户端和服务器端之间很容易定义合约,而且如果服务器端架构采用面向服务的方式,那么使用 XML 也可以允许非 Ajax 客户机使用您提供的数据。

我将考虑从 Java 对象产生 XML 数据的三种方法,并讨论每种方法的优劣。

自行进行序列化

首先,可以从对象图以编程的方式生成 XML。这种方式可以简单到只是在每个 JavaBean 类中实现 toXml() 方法即可。然后就可以选择合适的 XML API,让每个 bean 提供表示自己状态的元素,并递归地对自己的成员调用对象图。显然,这种方式无法扩展到大量的类,因为每个类都需要专门编写自己的 XML 生成代码。从好的方面来看,这是一个实现起来简单的方式,没有额外的配置支出或者更复杂的构建过程支出,任何 JavaBean 图都可以只用几个调用就变成 XML 文档。

public Element toXml() {

  Element elOrder = new Element("order");

elOrder.setAttribute("id",id);

  elOrder.setAttribute("cost",getFormattedCost());

  Element elDate = new Element("date").addContent(date);

elOrder.addContent(elDate);

  Element elItems = new Element("items");

for (Iterator<Item> iter =

items.iterator() ; iter.hasNext() ; ) {

elItems.addContent(iter.next().toXml());

}

elOrder.addContent(elItems);

  return elOrder;

}

在这里可以看到用 JDOM 创建元素、使用属性和添加元素内容有多么简单。递归地调用复合 JavaBean 的 toXml() 方法是为了取得它们子图的 Element 表示。例如,items 元素的内容是通过调用 Order 聚合的每个 Item 对象上的 toXml() 得到的。

一旦所有的 JavaBean 都实现了 toXml() 方法,那么把任意对象图序列化成 XML 文档并返回给 Ajax 客户机就简单了,如清单 2 所示。

清单 2. 从 JDOM 元素生成 XML 响应

public void doGet(HttpServletRequest req, HttpServletResponse res)

throws java.io.IOException, ServletException {

    String custId = req.getParameter("username");

Customer customer = getCustomer(custId);

    Element responseElem = customer.toXml();

Document responseDoc = new Document(responseElem);

    res.setContentType("application/xml");

new XMLOutputter().output(responseDoc,res.getWriter());

}

JDOM 再次把工作变得非常简单。只需要在对象图返回的 XML 元素外面包装一个 Document,然后用 XMLOutputter 把文档写入 servlet 响应即可。清单 3 显示了用这种方式生成的 XML 示例,用 JDOM Format.getPrettyFormat() 对 XMLOutputter 进行初始化,格式化得非常好。在这个示例中,顾客只做了一个订单,包含两个商品。

可以看到,org.json API 非常简单。 JSONObject 代表 JavaScript 对象(即联合数组),有不同的 put() 方法,方法接受的 String 键和值是原生类型、String 类型或其他 JSON 类型。JSONArray 代表索引数组,所以它的 put() 方法只接受一个值。请注意在清单 8 中,创建 jsonItems 数组,然后再用 put() 把它附加到 json 对象上;可以用另外一种方法做这项工作,就是对每个项目调用 json.accumulate("items",iter.next().toJSONObject());。accumulate() 方法与 put() 类似,区别在于它把值添加到按照键进行识别的索引数组。

清单 9 显示了如何序列化 JSONObject 并把它写入 servlet 响应。

清单 9. 从 JSONObject 生成序列化的 JSON 响应

public void doGet(HttpServletRequest req, HttpServletResponse res)

throws java.io.IOException, ServletException {

 String custId = req.getParameter("username");

Customer customer = getCustomer(custId);

 res.setContentType("application/x-json");

res.getWriter().print(customer.toJSONObject());

}

可以看到,它实际上什么也没有做。在这里隐式调用的 JSONObject 的 toString() 方法做了所有工作。请注意,application/x-json 内容类型还有一点不确定 —— 在编写这篇文章的时候,关于 JSON 应当属于什么 MIME 类型还没有定论。但是,目前 application/x-json 是合理的选择。清单 10 显示了这个 servlet 代码的示例响应。

清单 10. Customer bean 的 JSON 表示


"orders": [

{

"items": [

{

"price": "$49.99",

"description": "512 Megabyte Type 1 CompactFlash card.

Manufactured by Oolong Industries",

"name": "Oolong 512MB CF Card",

"id": "i-55768"

},

{

"price": "$299.99",

"description": "7.2 Megapixel digital camera featuring six

shooting modes and 3x optical zoom. Silver.",

"name": "Fujak Superpix72 Camera",

"id": "i-74491"

}

],

"date": "08-26-2005",

"cost": "$349.98",

"id": "o-11123"

}

],

"realname": "James Hyrax",

"username": "jimmy66"

}

处理的最后一步是把在客户端把 JSON 数据变成 JavaScript 对象。这可以通过对 eval() 的简单调用实现,这个函数可以即时地解释包含 JavaScript 表达式的字符串。清单 11 把 JSON 响应转变成 JavaScript 对象图,然后执行清单 5 的任务,从顾客的最后一次订单中得到第一个商品的名称。

清单 11. 评估 JSON 响应

var jsonExpression = "(" + req.responseText + ")";

var customer = eval(jsonExpression);

// Find name of first item in customer's last order

var lastOrder = customer.orders[customer.orders.length-1];

var name = lastOrder.items[0].name;

比较清单 11 和 清单 5 可以发现使用 JSON 的客户端的优势。如果在 Ajax 项目中要在客户端对许多复杂的服务器响应进行导航,那么 JSON 可能适合您的需要。JSON 和 XMLHttpRequest 结合还会让 Ajax 交互看起来更像 RPC 调用而不是 SOA 请求,这对应用程序的设计可能会有意义。在下一篇文章中,我要研究的框架,就是明确地为了让 JavaScript 代码对服务器端对象进行远程方法调用而设计的。

JSON 的不足

JSON 也有它的不足。使用这里介绍的 JSON 方式,就没有办法针对每个请求对对象的序列化进行裁剪,所以不需要的字段可能经常会在网络上发送。另外,添加 toJSONObject() 方法到每个 JavaBean,可伸缩性不太好,虽然用内省和标注编写一个通用的 JavaBean 到 JSON 的序列化器可能很简单。最后,如果服务器端代码是面向服务的,没有单独针对处理 Ajax 客户请求调整过,那么由于对 XML 一致的支持,XML 会是更好的选择。

比较序列化技术

现在已经看到了把 Java 状态传输到 Ajax 客户端的五种不同技术。我讨论了自行手工编码 XML 序列化、通过代码生成的 XML 绑定、通过映射机制的 XML 绑定、基于模板的 XML 生成以及手工编码到 JSON 的序列化。每种技术都有自己的优势和不足,分别适用于不同的应用程序架构。

为了总结每种方式的优势与不足,表 1 从六个方面进行了粗略的评分:

可伸缩性

描述技术适应大量数据类型的容易程度。对于每个附加类型,编码和配置工作量是否会增长?

易于集成

评估把技术集成到项目的简单程度。是否需要更加复杂的构建过程?是否增加了部署的复杂性?

Java 类 API

描述以指定方式处理服务器端 Java 对象的容易程度。是可以编写普通的 bean,还是不得不处理笨拙的文档表示?

对输出的控制

描述对类的序列化表示控制的精确程度。

视图灵活性

评估从同一组对象是否可以创建不同的、定制的数据序列化。

客户端数据访问

描述 JavaScript 代码处理服务器响应数据的难易程度。

<

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: