在之前的博文《geometry:MySQL的空间数据类型(Spatial Data Type)与JTS(OSGeo)类型之间的序列化和反序列化》中,实现了对MySQL数据库存储的WKB数据到JTS Geometry对象之间的转换。
当我们从数据库中得到的Geometry对象后,我们需要把它提供给前端时,就需要将它转为JSON格式,或从前端将JSON数据反序列化为Geometry对象。本文说明使用JSON工具库Fastjson如何实现这个过程。
JTS Geometry对象不是标准的Java Bean不能自动被Fastjson执行序列化和反序列化。所以我们需要为 Geometry对象实现自定义的序列化器和反序列化器。
我的实现方式就是将Geometry对象序列化为字符串,即WKT(Well-Known Text)字符串。
JTS库依赖引入
<dependency>
<groupId>com.vividsolutions</groupId>
<artifactId>jts</artifactId>
<version>1.13</version>
</dependency>
ObjectSerializer,ObjectDeserializer接口实现
为所有Geometry类实现的序列化和反序列化
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* JTS几何对象FASTJSON序列化反序列化实现<br>
* 参见 <a href="https://www.osgeo.org/projects/jts/">JTS Topology Suite</a>
* @author guyadong
* @param <T>
*/
public class GeometryCodec<T extends Geometry> implements ObjectSerializer, ObjectDeserializer {
/**
* 将geometry类型序列化为WKT JSON字符串
*/
@SuppressWarnings("unchecked")
@Override
public void write(JSONSerializer jsonSerializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
T geom = (T) object;
jsonSerializer.write(geom.toString());
}
/**
* 将WKT字符串反序列为{@link Geometry}
*/
@SuppressWarnings("unchecked")
@Override
public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Object value = parser.parse();
if (value == null) {
return null;
} else {
String wkt = (String) value;
WKTReader reader = new WKTReader();
try {
return (T) reader.read(wkt);
} catch (ParseException e) {
throw new JSONException(e.getMessage(),e);
}
}
}
@Override
public int getFastMatchToken() {
return 0;
}
}
PointCodec
基于GeometryCodec实现Point的序列化和反序列化器
import com.vividsolutions.jts.geom.Point;
/**
* @author guyadong
*/
public class PointCodec extends GeometryCodec<Point>{
public static final PointCodec INSTANCE = new PointCodec();
public PointCodec() {
}
}
这个类很简单,如果只为了创建Point对应的序列化和反序列化实例,其实可以用new GeometryCodec<Point>()
来代替,
但定义了这个类,还可以以注解形式使用在类成员字段上,定义成员字段的序列化和反序列化方式
例如:
@com.alibaba.fastjson.annotation.JSONField(
serializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class,
deserializeUsing=gu.sql2java.geometry.fastjson.PointCodec.class)
private com.vividsolutions.jts.geom.Point spot;
调用示例
import static org.junit.Assert.*;
import org.junit.Test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.vividsolutions.jts.geom.Point;
public class GeometryFastjsonTest {
@Test
public void test() {
try {
/** JSON 格式字符串,保存WKT格式的坐标数据 */
String jsonWKT = "\"POINT (1.75 -1.222)\"";
System.out.printf("jsonWKT \t%s\n",jsonWKT);
SerializeConfig.globalInstance.put(Point.class, PointCodec.INSTANCE);
ParserConfig.global.putDeserializer(Point.class, PointCodec.INSTANCE);
/** 反序列化为Point对象 */
Point deserialized = JSON.parseObject(jsonWKT,Point.class);
System.out.printf("deserialized\t%s\n",deserialized.toText());
/** 序列化为JSON 字符串 */
String serialized = JSON.toJSONString(deserialized);
System.out.printf("serialized \t%s\n",serialized);
assertTrue(jsonWKT.equals(serialized));
} catch (Exception e) {
e.printStackTrace();
assertTrue(false);
}
}
}
输出
jsonWKT “POINT (1.75 -1.222)”
deserialized POINT (1.75 -1.222)
serialized “POINT (1.75 -1.222)”
完整代码参见我的码云仓库:https://gitee.com/l0km/sql2java/tree/dev/sql2java-base/src/main/java/gu/sql2java/geometry/fastjson