JSON(JavaScript Object Notation)是一个很受欢迎的代替以XML的数据格式传输到Web浏览器的数据交换格式。flexjson是一个轻量级的library用来序列化Java对象到JSON的。有什么不同flexjson是它的控制哪些得到系列化,让双方深与浅拷贝的对象。大多数JSON的序列化模仿对象序列化library,并设法序列化整个对象图,变成JSON的数据交换格式。这个问题的成因是当您想要一个被连接的对象模型在您的服务器里, 但您不能寄发那个对象模型到客户因为序列化library将设法送整个对象图。这使它很难针对模型创造对象和序列化那个模型片断对客户不需寄送一切。其他JSON的library,你必须创造大量的样板文件代码你把你的对象转换成JSON的对象。在这方面,我不应该来解释一下,要尽量简短,但我只想说,我最讨厌样板文件转换代码! flexjson试图解决上述两个问题提供了更高层次的API,我敢说的DSL指明你的意图。为了探索flexjson我们将使用以下的数据模式。让我们说说正在建设的PIM或联络管理应用。这里有几个class,我们可以看到它们之间的关系。
在上述图表,你可以看到Person中可以有很多的Phone和很多的Address。虽然这是个简单对象模型,这将有助于我们示范flexjson的用法。
flexjson采取了不同的做法,让你方便地控制序列化的深度。它与hibernate有个十分相似的概念延迟加载,允许你有一个连接对象模型,但控制哪些对象被加载了是在你的数据库之外执行。让我们来看一个简单的例子,让你感觉一下flexjson library如何工程。我们序列化一个Person的实例。我们不妨做到以下几点:
public String doSomething( Object arg1, ... ) {
Person p = ...load a person...;
JSONSerializer serializer = new JSONSerializer();
return serializer.serialize( p );
}
上述代码将会产生下列输出:
{
"class": "Person",
"name": "William Shakespeare",
"birthday": -12802392000000,
"nickname": "Bill"
}
看起来几乎像你所期待的。然而我们的Person对象包含了更多的属性比如姓名,生日,昵称,还有电话号码和地址?默认情况下flexjson序列化对象的直接属性。这只是一个浅层的代表对象。所有容器类都是没有系列化。或者说,一对多的关系和多对多的关系并没有序列化。是指对象引用系列化。还有多对一的关系和一对一的关系将被序列化。这一点与其他的library一样,像Hibernate和JPA默认是热切加载。但是我们希望包括phonenumbers属性,然后我们可以做到以下几点:
public String doSomething( Object arg1, ... ) {
Person p = ...load a person...;
return new JSONSerializer().include("phoneNumbers").serialize(p);
}
在这个例子中,我们将告诉序列化器对象包括的属性。容器类的系列化是浅层复制包括的对象内容。所以在我们的例子中,Person有个List类型的属性叫phonenumbers ,List类中包含Phong实例。这意味着flexjson将浅拷贝的Person,名单phonenumbers ,浅拷贝的每一个电话,例如内部的名单上。所以输出可能看起来是这样的:
{
"class": "Person",
"name": "William Shakespeare",
"birthday": -12802392000000,
"nickname": "Bill"
"phoneNumbers": [
{
"class": "Phone",
"name": "cell",
"number": "555-123-4567"
},
{
"class": "Phone",
"name": "home",
"number": "555-987-6543"
},
{
"class": "Phone",
"name": "work",
"number": "555-678-3542"
}
]
}
熟悉这个用法了?这实在是太简单。如果你想包含phonenumbers和address,你可以执行两次include方法,或者你可以包括两个参数,include方法使用了Java的新语法功能。我个人较喜欢因为我认为这将令程序短小且易于阅读。但是,那是你自己的选择。
public String doSomething( Object arg1, ... ) {
Person p = ...load a person...;
return new JSONSerializer().include("phoneNumbers", "addresses").serialize(p);
}
所以在这种情况下,我们将只能获得一个街,市,州,但不是邮政编码,因为这是一个对象引用。它采用一种简单的固定小点记法。这里是一个例子,包括邮编每个地址举例。
public String doSomething( Object arg1, ... ) {
Person p = ...load a person...;
return new JSONSerializer().include("phoneNumbers", "addresses.zipcode").serialize(p);
}
Flexjson 是足够聪明的知道你想要对象本身包含collection容器类和不包含collection容器类。它也足够聪明的知道地址引用也包括邮编,让您不用指定两次。你可以非常容易的使用点符号追寻你的对象图。
有一个候补的serialize( )方法允许您指定一个外部对象集合。也有一些JavaScript的library象EXTJS((previously known as YUI-EXT)要求这为他们的JSON 数据模型。不过,我并没有发现有任何JSON的library提供这种类型的系列化。下面是一个例子:
public String getPeople( Object arg1, ... ) {
List people = ...load a person...;
return new JSONSerializer().include("phoneNumbers").serialize("people", people);
}
上述代码将会产生下列输出:
{
"people" : [
{
"class": "Person",
"name": "Warren Buffet",
"birthday": -1241467200000,
"nickname": "Oracle of Omaha",
"phonNumbers" : [ ... ],
},
{
"class": "Person",
"name": "Steven Jobs",
"birthday": -468702000000,
"nickname": "Steve",
"phonNumbers" : [ ... ],
}
]
}
您不只可以包括,你还可以指定某些属性来排除它。exclude( )方法可以让你在序列化时排除某些属性。如果你有特殊的属性,你不想要发送到客户端像密码,或保密的数据,应该留在服务器上,这个功能就派上用场。
public String doSomething( Object arg1, ... ) {
User u = ...load a user...;
return new JSONSerializer().exclude("password").include("hobbies").serialize(p);
}
使用点“.”时exludes与includes在使用上有一个微妙的差异。如果你排除嵌套属性,就意味着其余的父对象也包括在内。如果执行exclude("head.rightEye.retinalScan")
。rightEye属性的retinalScan成员将被排除,但是rightEye和主要的属性将包括在内。原因是为了排除retinalScan属性,你必须包括rightEye成员。如果你没有的话,并不会改变任何东西,因为retinalScan本来并不打算列入摆在首位。另一种说法,它是唯一排斥的最后一个属性其他父属性均包括在内。
总是用这种方式做排除是很累赘的,尤其是如果你总是想要做某事被排除或包括。 flexjson提供了一种注解(Annotations)方法来表达。JSON注解(Annotations)可以使用标记包括一个对象的属性。注解(Annotations)可能被安置在方法或领域。。一个 很好的例子,Address的对象包含一个引用的Zipcode对象。因为邮政编码是每个地址的整体部分(在美国),这里我们使用注解(Annotations)方法来表达。因此,在我们的Address对象,我们可以做到以下几点:
public class Address {
private String name;
private String street;
private String city;
private String state;
private Zipcode zipcode;
@JSON
public Zipcode getZipcode() {
}
}
注解(Annotations)可提高安全性。采用这种方法,您让您的代码更DRY,并防止意外的安全漏洞。考虑如果我们存放密码的一个hashed 版本在我们的User对象。
public class User {
private String login;
private String hashedPassword;
private Date lastLogin;
@JSON(include=false)
public String getHashedPassword() {
}
}
您可以指定通配符在include() 和exclude() 方法调用时。这使得包括或排除几个属性变得非常容易。使用通配符去除类属性的情况非常普遍。使用计算机通配符它变得非常容易。例如:
public String doSomething( Object arg1, ... ) {
User u = ...load a user...;
return new JSONSerializer().exclude("*.class").serialize(p);
}
从类的所有路径结尾将被排除。通配符匹配您指定的所有深度。 那么无论你的对象路径多么深。 *.class排除配比对所有路径深度。 因此,如果flexjson序列化与“foo.bar.class”路径的属性“*”在“*.class”将匹配foo.bar。
通配符不扩展您在许多情况下序列化的深度。他们只操作您指定了的深度。这意味着如果您指定*.list,像路径foo.list将被序列化,但是foo.bar.list将不会被序列化。如果您想要foo.bar.list被序列化,您需要另一个个包含foo.bar的声明。以同样的标准象*.class。每个对象有类成员由于Object.getClass ()。 然而*.class不会扩大您的序列化的深度。有序列化深度将被扩展的案件。当做指定与递归结构时的通配符您必须小心。如果您有一个树结构并且做了在*.children的一包括它一直将扩展您的序列化的深度。通配符将扩展您的序列化的另一个案件“*”。 那是相同的象做一次深刻的序列化如此使用它小心。
Order of evaluation自1.5改变了。即然我们谈论了包括和排除再让我们谈论Order of evaluation。如果我包括一个属性我然后排除同样的属性将会发生什么? Flexjson将做什么?答案是包括和不包括正在评估,以便他们补充说。这对通配符的介绍是重要的。我想要序列化对象的二个属性。我可以做以下:
public String doSomething( Object arg1, ... ) {
User u = ...load a user...;
return new JSONSerializer().include("username").exclude("*").serialize(p);
}
上述代码将序列化用户对象的username属性,但是它将排除其他属性。如果我换一下include和exclude方法的顺序将排除所有的属性,并且username属性不会被包括的。这是为什么?这是因为在内部flexjson将有一份表达式的列表。它就像是下面的清单:
[ "*", "username" ];
Flexjson将访问User对象的每个领域,并且根据列表中的表达式规则来评估它。一旦它发现第一个匹配的就会停止下来。 在这种情况下“*”将匹配User对象的所有属性,并且它排除他们。因而不会到达“username”。
在1.2中增加了一个新的特性,使用deepSerialize ()方法进行对象的一次深刻的序列化。 与Serialize ()方法deepSerialize ()方法将使用包括,排除和注解来推测什么是您想要的。然而,包括通常是重复的除了在您想要代理佣金在领域的一个排除的注释的案件。深刻的连续广播不会如此将连载在您的图表的周期,如果您有定向关系说的双父母有一个孩子,并且孩子有一个父母。后面参考从孩子到父母在JSON产品不会包括。 这是同样浅连续广播的。 这一个快的例子:
public String doSomething( Object arg1, ... ) {
Person p = ...load a person...;
return new JSONSerializer().deepSerialize(p); // send the entire graph starting at person
}
应节制使用deepSerialize ()方法,你真正的了解什么是系列化的本质的唯一的情况下。深刻的序列化可能会发送大量数据,超过你期望的数据,如果你用它草率。
在1.5增加了一个扣人心弦的新的特点。假设我们有一些JSON文本数据,并且我们设置它进入到页面,但是这数据包含不友好的HTML。我的意思是你可能有象<, >或者&。我们需要用<, >, &替换我们的文本的那些部分。您怎么做?在先前版本你需要设置特定的getters/setters方法对你的域模型执行转换,你必须这样做在客户端。 噢是的,我肯定您可能做的更加离奇,但是这些是“cleanest”解答。
转换器通过让您指定转换的对象在与Flexjson的序列化论及这个问题对particpate。 Flexjson包括非常简单的HTMLEncoder为你做实体替换。这它怎么运作。
public String doSomething( Object arg1, ... ) {
EMail mail = ...load an email...;
return new JSONSerializer().transform( new HTMLEncoder(), "to", "cc", "bcc", "from").serialize(p);
}
在上述代码transform ()方法中注册了HTMLEncoder转换器为4个属性做序列化。
电子邮件通常有象以下在接收者属性:"Plug 1" <plug1@delasoul.com>。不幸地,HTML不识别“<“和“>”,因此您需要在HTML中转码。变换器方法支持点符号,但是它不支持通配符。这样做使查找转换器实体算法更加快速。
转换器是非常有趣,你可以利用它们解决各种困难。当您接受在新的wiki软件的减号符号时。使用转换器您可以在DB存放减号符号的文本,以后可以读取到HTML页面。
由于安全原因您只想要接受某些从用户发来的HTML标记。当送它通过JSON时,转换器能过滤您的对象的所有恶毒数据。转换器是非常有用的,并且Flexjson在将来将包括更多转换器。
最后, jsonserializer实例可以再序列化许多同一类型的对象。一旦你实例化对象,你可以再用它运行多个线程,只要你不要求包括或排除方法。通常这不是一个问题,因为你可能会遵循这一模式的实例:
public class PersonController {
JSONSerializer personSerializer;
public PersonController() {
personSerializer = new JSONSerializer().include("addresses.zipcdoe");
}
public String listPerson() {
Person p = ....;
return personSerializer.serialize( p );
}
public String editPerson() {
Person p = ....;
return personSerializer.serialize( p );
}
}