首先,可以从对象图以编程的方式生成 XML。这种方式可以简单到只是在每个 JavaBean 类中实现 toXml()
方法即可。然后就可以选择合适的 XML API,让每个 bean 提供表示自己状态的元素,并递归地对自己的成员调用对象图。显然,这种方式无法扩展到大量的类,因为每个类都需要专门编写自己的 XML 生成代码。从好的方面来看,这是一个实现起来简单的方式,没有额外的配置支出或者更复杂的构建过程支出,任何 JavaBean 图都可以只用几个调用就变成 XML 文档。
在本系列 前一篇文章 的示例代码中,我把 XML 标记字符串连接在一起,实现了 toXml()
方法。上次我就提到过,这是个糟糕的方法,因为它把确保标记配对、实体编码等工作的负担放在每个 toXml()
方法的代码中。在 Java 平台上有几个 XML API 可以替您做这些工作,这样您就可以把精力集中在 XML 的内容上。清单 1 用 JDOM API 实现了在线商店示例中表示订单的类中的 toXml()
(请参阅 图 1)。
清单 1. Order 类的 toXml() 的 JDOM 实现
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
进行初始化,格式化得非常好。在这个示例中,顾客只做了一个订单,包含两个商品。
清单 3. 代表顾客的 XML 文档
<?xml version="1.0" encoding="UTF-8"?> <customer username="jimmy66"> <realname>James Hyrax</realname> <orders> <order id="o-11123" cost="$349.98"> <date>08-26-2005</date> <items> <item id="i-55768"> <name>Oolong 512MB CF Card</name> <description>512 Megabyte Type 1 CompactFlash card. Manufactured by Oolong Industries</description> <price>$49.99</price> </item> <item id="i-74491"> <name>Fujak Superpix72 Camera</name> <description>7.2 Megapixel digital camera featuring six shooting modes and 3x optical zoom. Silver.</description> <price>$299.99</price> </item> </items> </order> </orders> </customer> |
有趣的是,清单 3 中的代码展示了让 JavaBean 把自己序列化为 XML 的一个主要不足。假设要用这个文档表示顾客的订单历史视图。在这种情况下,不太可能要显示每个历史订单中每个商品的完整说明,或者告诉顾客他或她自己的姓名。但是如果应用程序有一个 ProductSearch
类,它就是以 Item
bean 列表的形式返回搜索结果,那么在 Item
的 XML 表示中包含说明可能会有帮助。而且,Item
类上代表当前库存水平的额外字段,在产品搜索视图中可能就是需要显示的有用信息。但是,不管当前的库存水平是否与当前情况相关(比如对顾客的订单历史来说),这个字段都会从包含 Item
的任何对象图中序列化出来。
从设计的角度来看,这是数据模型与视图生成耦合的经典问题。每个 bean 只能用一种途径序列化自己,一成不变的方式意味着 Ajax 交互最终要交换它们不需要交换的数据,因此造成客户端代码要从文档中找到需要的信息更加困难,而且也会增加带宽消耗和客户端的 XML 解析时间。这种耦合的另一个后果就是 XML 的语法不能脱离 Java 类独立变化。例如,对顾客文档的方案做修改,可能会影响多个 Java 类,造成它们也不得不做修改和重新编译。
我稍后会解决这些问题,但是首先来看一个对自行序列化方式的可伸缩性问题的解决方案:XML 绑定框架。