Skip to content

Instantly share code, notes, and snippets.

@seraphy
Created June 25, 2019 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seraphy/9e67565d1d1ae0251aadf034531370a9 to your computer and use it in GitHub Desktop.
Save seraphy/9e67565d1d1ae0251aadf034531370a9 to your computer and use it in GitHub Desktop.
データポイントのような大量データを効率よくJSONで扱う実装例。簡略化した文字列でJSON表現とする方法と、リストをバイナリにして単一のbase64文字列にする方法。
package jp.seraphyware.example.java8learn.json;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
/**
* データポイントのような大量データを効率よくJSONで扱うために、
* ・ 簡略化した文字列でオブジェクトを表現する方法
* ・ リストごとバイナリにして表現する方法
* の実装例
*/
public class CustomSerializeExample {
/**
* シンプルな座標データ
*/
public static class DataPoint {
double x;
double y;
boolean flag;
public DataPoint() {
this(0, 0, false);
}
public DataPoint(double x, double y) {
this(x, y, false);
}
public DataPoint(double x, double y, boolean flag) {
this.x = x;
this.y = y;
this.flag = flag;
}
/**
* JSONのデシリアライズのために必要な文字列を受け取るコンストラクタ。
* toStringと対となる。
* @param str
*/
public DataPoint(String str) {
if (str == null || str.length() == 0) {
return;
}
String[] tokens = str.split(",");
if (tokens.length != 2 && tokens.length != 3) {
throw new IllegalArgumentException("invalid format:" + str);
}
double x = Double.parseDouble(tokens[0]);
double y = Double.parseDouble(tokens[1]);
boolean flag;
if (tokens.length == 3) {
flag = Boolean.parseBoolean(tokens[2]);
} else {
flag = false;
}
this.x = x;
this.y = y;
this.flag = flag;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
/**
* 文字列化。
* コンパクトなJSONの文字列表現としてフィールドではなく単純な文字列にしておく。
*/
@Override
@JsonValue
public String toString() {
if (!flag) {
return x + "," + y;
}
return x + "," + y + "," + flag;
}
}
/**
* 座標データのリストを保持するクラス
*/
public static class Foo {
/**
* カスタマイズしていない素のリストによるデータポイントの保持リスト
*/
private final List<DataPoint> dps = new ArrayList<>();
/**
* Jacksonのシリアライズ・デシリアライズをカスタマイズしているデータポイントの保持リスト。
* JSONのリストとしてではなく、1つのgzip圧縮されたバイナリのbase64表現にしている。
*/
@JsonSerialize(using = ListDataPointSerializer.class)
@JsonDeserialize(using = ListDataPointDeserializer.class)
private final List<DataPoint> dps2 = new ArrayList<>();
public List<DataPoint> getDps() {
return dps;
}
public List<DataPoint> getDps2() {
return dps2;
}
@Override
public String toString() {
return "Foo [dps=" + dps + ", dps2=" + dps2 + "]";
}
}
/**
* DataPointのリストをバイナリ形式のgzip圧縮したbase64文字列として表現する。
*/
private static class ListDataPointSerializer extends JsonSerializer<List<DataPoint>> {
@Override
public void serialize(List<DataPoint> value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (GZIPOutputStream os = new GZIPOutputStream(bos);
DataOutputStream dos = new DataOutputStream(os)) {
if (value == null) {
dos.writeInt(-1);
} else {
dos.writeInt(value.size());
for (DataPoint dp : value) {
dos.writeDouble(dp.getX());
dos.writeDouble(dp.getY());
dos.writeBoolean(dp.isFlag());
}
}
}
gen.writeString(Base64.getEncoder().encodeToString(bos.toByteArray()));
}
}
/**
* base64文字列を受け取りgzip圧縮されたDataPointのリストとして復元する
*/
private static class ListDataPointDeserializer extends JsonDeserializer<List<DataPoint>> {
@Override
public List<DataPoint> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String text = p.getText();
if (text == null || text.length() == 0) {
return null;
}
byte[] raw = Base64.getDecoder().decode(text);
ByteArrayInputStream bis = new ByteArrayInputStream(raw);
try (GZIPInputStream is = new GZIPInputStream(bis);
DataInputStream dis = new DataInputStream(is)) {
int len = dis.readInt();
if (len < 0) {
return null;
}
List<DataPoint> dps = new ArrayList<>();
for (int idx = 0; idx < len; idx++) {
double x = dis.readDouble();
double y = dis.readDouble();
boolean flag = dis.readBoolean();
DataPoint dp = new DataPoint(x, y, flag);
dps.add(dp);
}
return dps;
}
}
}
/**
* 実験コード
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// データの作成
Foo foo = new Foo();
for (int i = 0; i < 10; i++) {
DataPoint dp = new DataPoint();
dp.setX(i);
dp.setY(2000 + i);
dp.setFlag(i % 3 == 0);
foo.getDps().add(dp);
foo.getDps2().add(dp);
}
// JSONへのシリアライズ
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(foo);
System.out.println("json=" + json);
// 復元
Foo foo2 = mapper.readValue(json, Foo.class);
System.out.println("foo2=" + foo2);
}
}
json={"dps":["0.0,2000.0,true","1.0,2001.0","2.0,2002.0","3.0,2003.0,true","4.0,2004.0","5.0,2005.0","6.0,2006.0,true","7.0,2007.0","8.0,2008.0","9.0,2009.0,true"],"dps2":"H4sIAAAAAAAAAGNgYOBigAKH+Q5gmtH+A0zABcqAq/CAMjhgAj4QLQ4CMIEAKEMEJhACZUjABCKgWmRgAjFQhgJMIAHKUIIJpEC0AACGoDNyrgAAAA=="}
foo2=Foo [dps=[0.0,2000.0,true, 1.0,2001.0, 2.0,2002.0, 3.0,2003.0,true, 4.0,2004.0, 5.0,2005.0, 6.0,2006.0,true, 7.0,2007.0, 8.0,2008.0, 9.0,2009.0,true], dps2=[0.0,2000.0,true, 1.0,2001.0, 2.0,2002.0, 3.0,2003.0,true, 4.0,2004.0, 5.0,2005.0, 6.0,2006.0,true, 7.0,2007.0, 8.0,2008.0, 9.0,2009.0,true]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment