7.4 JSON支持
Limax提供完整的JSON支持(Java,C#,C++,Lua版本均提供与Javascript一致的JSON支持,使用上最大限度保证与javascript相同),便于同其它支持JSON的第三方系统进行交互。
-
7.4.1 编码
Javascript JSON.stringify(obj) Java String limax.codec.JSON.stringify(Object obj); C# string limax.codec.JSON.stringify(object obj); C++ std::string limax::JSON::stringify(const T& object);
std::string limax::JSON::stringify(const char *obj);
std::string limax::JSON::stringify(std::shared_ptr<limax::JSON> obj);
Lua JSON.stringify(obj) C++,Lua版本中的串应该使用UTF8编码。
-
类型映射
Javascript Java C# C++ Lua Number byte
Byte
sbyte
SByte
int8_t
Number byte
Byte
uint8_t short
Short
short
Int16
int16_t
ushort
UInt16
uint16_t int
Integer
AtomicInteger
int
Int32
int32_t uint
UInt32
uint32_t long
Long
AtomicLong
long
Int64
int64_t ulong
UInt64
uint64_t float
Float
float
Float
float double
Double
double
Double
double boolean boolean
Boolean
AtomicBoolean
bool
bool
bool boolean string char
Character
String
char
Char
string
String
char
const char*
const std::string&
string Array java.reflect.Array
Collection
IEnumerable std::list<T>
std::vector<T>
std::deque<T>
std::unordered_set<T>
std::set<T>
table object JSONSerializable
JSONMarshal
Map
JSONSerializable
JSONMarshal
IDictionary
JSONMarshal
std::unordered_map<K,V>
std::map<K,V>
table null null null JSON
JSON
const JSON&
std::shared_ptr<JSON>
注意,JSON规范对应的类型object,在之后文档中写作JSON/Object,避免与Java, C#, C++实现的JSON类构造出来的对象JSON Object混淆。数组就写作JSON/Array, 以此类推。这些通称JSON元件。
1. Java的java.reflect.Array表示支持原生数组,Collection对应了各种集合容器。
2. C#所有容器,原生数组均派生自IEnumerable,所以编码时优先IDictionary检测,区分出是否需要按JSON/Object编码。
3. C++版本不支持任何类型的原生数组,或者说不支持指针方式指向的对象,const char*的支持只是一种语法糖,这种参数进入编码器前,立刻被转换成std::string。既然不支持指针方式指向的对象,也就意味着不会编码生成null,除非在JSONBuilder上使用null()方法硬编码一个null,或者进行中继,见后。
4. Lua中table的使用乱七八糟,编码器通过#table>0的方法区分JSON/Array与JSON/Object,如果需要正确编码,不要在一个table上混用数组操作和对象操作。Lua的nil解释为null。
5. null不利于各语言版本互操作,为了减少不必要的麻烦,尽可能避免使用。
6. 关联容器Map,IDictionary,std::unordered_map<K,V>,std::map<K,V>,table被编码为JSON/Object时,其中的key被强制转换为字符串,按JSON/String编码。 如果容器定义了非string类型的key, 应该小心验证强制转换的结果是否符合预期。
7. 如果编码结果提交给Javascript处理,必须注意到Javascript的Number最多支持53位整型。
8. 表格的最后一行没有Javascript的对应,Java,C#,C++,执行JSON中继任务时使用,见后。
-
代码生成实现(Java,C#,C++)
Java,C#,C++三种类型化语言,使用JSONMarshal与JSONBuilder进行交互完成JSON/Object的编码。limax环境下,具体对象的JSONMarshal.marshal方法可以通过代码生成实现。 只要在bean, cbean,xbean,protocol的xml描述中正确使用json属性即可,例如:
<bean name="JChild" json="true"> <variable name="val" type="string"/> </bean> <bean name="JBean" json="true"> <variable name="intset" type="set" value="int"/> <variable name="child" type="JChild"/> <variable name="ignore" type="binary" json="false"/> </bean>
使用生成的JBean类型的对象,调用JSON类的静态方法stringify,即可获得类似如下的输出结果:
{"intset":[1,2],"child":{"val":"it's child"}}
bean,cbean,xbean,protocol元素上设定属性json="true",允许为该类型生成JSON/Object编码代码, 同时默认下属variable元素的json属性为true, 除非为variable单独设置属性json="false",关闭该元素对应字段的代码成。上例中JBean.ignore字段,在编码时被忽略。
代码生成时,将严格检查xml描述,确保生成有效的JSON/Object编码代码,否则抛出异常,停止生成。具体来说,3种情况:
1. 使用了binary类型
2. 使用了any类型
3. 引用的bean没有开启json="true"
-
反射方式实现(Java,C#)
Java,C#支持反射,只需要在定义类的时候实现JSONSerializable标签接口即可,支持该标签的对象在编码时被直接解释为JSON/Object,字段名即是对象的key, 例如定义两个Java类:
public class JChild implements JSONSerializable { private String val = "it's child"; } public class JBean implements JSONSerializable { private int intset[] = new int[] { 1, 2 }; private JChild child = new JChild(); private transient Octets ignore; }
System.out.println(JSON.stringify(new JBean())) 即可输出与上面例子相同的结果。
在这里Java关键字transient,阻止了ignore字段的编码,C#没有类似特性,必须注意。特别的,C#类中定义的属性字段在反射时将获得一个C#内部编码的字段名,需要JSON编码的类尽量避免使用属性字段。
编码器执行反射时,只考虑对象所属类本身,不考虑基类,即便基类同样实现了JSONSerializable接口。
如果被反射的字段类型没有在类型映射一节的表格中列出,编码时将抛出JSONException异常,编码失败。详见后节——异常规范。
-
关联容器方式实现(Java,C#,C++)
直接对一个关联容器编码即可。
例如Java代码:
Map<String, Object> jchild = new HashMap<>(); jchild.put("val", "it's child"); Map<String, Object> jbean = new HashMap<>(); jbean.put("intset", new int[]{1,2}); jbean.put("child", jchild); System.out.println(JSON.stringify(jbean));
可以输出与上面例子相同的结果。
C++中基本类型缺少一个公共基类,所以上述结果无法实现。
std::unordered_map<std::string, std::string> jchild; jchild.insert(std::make_pair("val", "it's child")); std::unordered_map<std::string, std::unordered_map<std::string, std::string>> jbean; jbean.insert(std::make_pair("child", jchild)); printf("%s\n", JSON::stringify(jbean).c_str());
只能拼凑出规整的结果:
{"child":{"val":"it's child"}}
由上可见:
1. 对于C++,多数情况下只有使用代码生成方式,或者参考生成的代码,手工实现。
2. 关联容器方式比生成代码方式,反射方式可读性差很多,非特殊情况不值得使用。
-