mirror of
https://github.com/valitydev/geck.git
synced 2024-11-06 09:45:24 +00:00
Ft/bj 76/json writer (#3)
* BJ-76: Add json writer * BJ-76: Add stack changes * BJ-76: Add check in endKey * BJ-76: Change name method interaction * BJ-76: Change ids names in stack * BJ-76: Add enum, check nulls and more
This commit is contained in:
parent
ebbc8bacfd
commit
a0a4fd0dba
@ -1,14 +1,26 @@
|
|||||||
package com.rbkmoney.kebab;
|
package com.rbkmoney.kebab;
|
||||||
|
|
||||||
|
import com.rbkmoney.kebab.serializer.TBaseSerializer;
|
||||||
|
import com.rbkmoney.kebab.writer.JsonStructWriter;
|
||||||
import org.apache.thrift.TBase;
|
import org.apache.thrift.TBase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by tolkonepiu on 24/01/2017.
|
* Created by tolkonepiu on 24/01/2017.
|
||||||
*/
|
*/
|
||||||
public class Kebab<T extends TBase> {
|
public class Kebab<T extends TBase> {
|
||||||
|
|
||||||
public String toJson(T src) {
|
public String toJson(T src) {
|
||||||
throw new UnsupportedOperationException("under contruction");
|
StringWriter writer = new StringWriter();
|
||||||
|
Serializer serializer = new TBaseSerializer();
|
||||||
|
try {
|
||||||
|
serializer.write(new JsonStructWriter(writer), src);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
return writer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] toMsgPack(T src) {
|
public byte[] toMsgPack(T src) {
|
||||||
|
@ -9,6 +9,7 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public enum ThriftType {
|
public enum ThriftType {
|
||||||
|
|
||||||
|
NULL(-1),
|
||||||
BOOLEAN(TType.BOOL),
|
BOOLEAN(TType.BOOL),
|
||||||
BYTE(TType.BYTE),
|
BYTE(TType.BYTE),
|
||||||
DOUBLE(TType.DOUBLE),
|
DOUBLE(TType.DOUBLE),
|
||||||
@ -20,7 +21,8 @@ public enum ThriftType {
|
|||||||
LIST(TType.LIST),
|
LIST(TType.LIST),
|
||||||
SET(TType.SET),
|
SET(TType.SET),
|
||||||
MAP(TType.MAP),
|
MAP(TType.MAP),
|
||||||
STRUCT(TType.STRUCT);
|
STRUCT(TType.STRUCT),
|
||||||
|
BINARY(21);
|
||||||
|
|
||||||
int code;
|
int code;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.rbkmoney.kebab.StructWriter;
|
|||||||
import com.rbkmoney.kebab.ThriftType;
|
import com.rbkmoney.kebab.ThriftType;
|
||||||
import org.apache.thrift.TBase;
|
import org.apache.thrift.TBase;
|
||||||
import org.apache.thrift.TFieldIdEnum;
|
import org.apache.thrift.TFieldIdEnum;
|
||||||
|
import org.apache.thrift.TFieldRequirementType;
|
||||||
import org.apache.thrift.meta_data.*;
|
import org.apache.thrift.meta_data.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -19,26 +20,38 @@ public class TBaseSerializer implements Serializer<TBase> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(StructWriter out, TBase value) throws IOException {
|
public void write(StructWriter out, TBase value) throws IOException {
|
||||||
out.beginStruct();
|
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
out.nullValue();
|
out.nullValue();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeStruct(out, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeStruct(StructWriter out, TBase value) throws IOException {
|
||||||
|
out.beginStruct();
|
||||||
|
|
||||||
TFieldIdEnum[] tFieldIdEnums = value.getFields();
|
TFieldIdEnum[] tFieldIdEnums = value.getFields();
|
||||||
Map<TFieldIdEnum, FieldMetaData> fieldMetaDataMap = value.getFieldMetaData();
|
Map<TFieldIdEnum, FieldMetaData> fieldMetaDataMap = value.getFieldMetaData();
|
||||||
|
|
||||||
for (TFieldIdEnum tFieldIdEnum : tFieldIdEnums) {
|
for (TFieldIdEnum tFieldIdEnum : tFieldIdEnums) {
|
||||||
|
FieldMetaData fieldMetaData = fieldMetaDataMap.get(tFieldIdEnum);
|
||||||
if (value.isSet(tFieldIdEnum)) {
|
if (value.isSet(tFieldIdEnum)) {
|
||||||
out.name(tFieldIdEnum.getFieldName());
|
out.name(tFieldIdEnum.getFieldName());
|
||||||
FieldMetaData fieldMetaData = fieldMetaDataMap.get(tFieldIdEnum);
|
|
||||||
write(out, value.getFieldValue(tFieldIdEnum), fieldMetaData.valueMetaData);
|
write(out, value.getFieldValue(tFieldIdEnum), fieldMetaData.valueMetaData);
|
||||||
|
} else if (fieldMetaData.requirementType == TFieldRequirementType.REQUIRED) {
|
||||||
|
throw new IllegalStateException(String.format("Field '%s' is required and must not be null", tFieldIdEnum.getFieldName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.endStruct();
|
out.endStruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void write(StructWriter out, Object object, FieldValueMetaData fieldValueMetaData) throws IOException {
|
private void write(StructWriter out, Object object, FieldValueMetaData fieldValueMetaData) throws IOException {
|
||||||
|
if (object == null) {
|
||||||
|
out.nullValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ThriftType type = ThriftType.findByCode(fieldValueMetaData.getType());
|
ThriftType type = ThriftType.findByCode(fieldValueMetaData.getType());
|
||||||
boolean isBinary = fieldValueMetaData.isBinary();
|
boolean isBinary = fieldValueMetaData.isBinary();
|
||||||
|
|
||||||
@ -67,17 +80,20 @@ public class TBaseSerializer implements Serializer<TBase> {
|
|||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
out.value((double) object);
|
out.value((double) object);
|
||||||
break;
|
break;
|
||||||
|
case ENUM:
|
||||||
|
out.value(object.toString());
|
||||||
|
break;
|
||||||
case LIST:
|
case LIST:
|
||||||
write(out, (List) object, (ListMetaData) fieldValueMetaData);
|
writeList(out, (List) object, (ListMetaData) fieldValueMetaData);
|
||||||
break;
|
break;
|
||||||
case SET:
|
case SET:
|
||||||
write(out, (Set) object, (SetMetaData) fieldValueMetaData);
|
writeSet(out, (Set) object, (SetMetaData) fieldValueMetaData);
|
||||||
break;
|
break;
|
||||||
case MAP:
|
case MAP:
|
||||||
write(out, (Map) object, (MapMetaData) fieldValueMetaData);
|
writeMap(out, (Map) object, (MapMetaData) fieldValueMetaData);
|
||||||
break;
|
break;
|
||||||
case STRUCT:
|
case STRUCT:
|
||||||
write(out, (TBase) object);
|
writeStruct(out, (TBase) object);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException(String.format("Type '%s' not found", type));
|
throw new IllegalStateException(String.format("Type '%s' not found", type));
|
||||||
@ -85,7 +101,7 @@ public class TBaseSerializer implements Serializer<TBase> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void write(StructWriter out, Set objectSet, SetMetaData metaData) throws IOException {
|
private void writeSet(StructWriter out, Set objectSet, SetMetaData metaData) throws IOException {
|
||||||
out.beginList(objectSet.size());
|
out.beginList(objectSet.size());
|
||||||
for (Object object : objectSet) {
|
for (Object object : objectSet) {
|
||||||
write(out, object, metaData.getElementMetaData());
|
write(out, object, metaData.getElementMetaData());
|
||||||
@ -93,7 +109,7 @@ public class TBaseSerializer implements Serializer<TBase> {
|
|||||||
out.endList();
|
out.endList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void write(StructWriter out, List objectList, ListMetaData metaData) throws IOException {
|
private void writeList(StructWriter out, List objectList, ListMetaData metaData) throws IOException {
|
||||||
out.beginList(objectList.size());
|
out.beginList(objectList.size());
|
||||||
for (Object object : objectList) {
|
for (Object object : objectList) {
|
||||||
write(out, object, metaData.getElementMetaData());
|
write(out, object, metaData.getElementMetaData());
|
||||||
@ -102,7 +118,7 @@ public class TBaseSerializer implements Serializer<TBase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void write(StructWriter out, Map objectMap, MapMetaData metaData) throws IOException {
|
private void writeMap(StructWriter out, Map objectMap, MapMetaData metaData) throws IOException {
|
||||||
out.beginMap(objectMap.size());
|
out.beginMap(objectMap.size());
|
||||||
for (Map.Entry entry : (Set<Map.Entry>) objectMap.entrySet()) {
|
for (Map.Entry entry : (Set<Map.Entry>) objectMap.entrySet()) {
|
||||||
out.beginKey();
|
out.beginKey();
|
||||||
|
@ -1,116 +1,263 @@
|
|||||||
package com.rbkmoney.kebab.writer;
|
package com.rbkmoney.kebab.writer;
|
||||||
|
|
||||||
|
import com.rbkmoney.kebab.ByteStack;
|
||||||
import com.rbkmoney.kebab.StructWriter;
|
import com.rbkmoney.kebab.StructWriter;
|
||||||
|
import com.rbkmoney.kebab.ThriftType;
|
||||||
|
import com.rbkmoney.kebab.exception.BadFormatException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by tolkonepiu on 27/01/2017.
|
* Created by tolkonepiu on 27/01/2017.
|
||||||
*/
|
*/
|
||||||
public class JsonStructWriter implements StructWriter {
|
public class JsonStructWriter implements StructWriter {
|
||||||
|
|
||||||
|
static final byte EMPTY_STRUCT = 1;
|
||||||
|
|
||||||
|
static final byte NONEMPTY_STRUCT = 2;
|
||||||
|
|
||||||
|
static final byte EMPTY_LIST = 3;
|
||||||
|
|
||||||
|
static final byte NONEMPTY_LIST = 4;
|
||||||
|
|
||||||
|
static final byte EMPTY_MAP = 5;
|
||||||
|
|
||||||
|
static final byte NONEMPTY_MAP = 6;
|
||||||
|
|
||||||
|
static final byte JSON_NAME = 7;
|
||||||
|
|
||||||
|
static final byte EMPTY_DOCUMENT = 8;
|
||||||
|
|
||||||
|
static final byte NONEMPTY_DOCUMENT = 9;
|
||||||
|
|
||||||
|
private final Writer out;
|
||||||
|
|
||||||
|
private ByteStack stack = new ByteStack();
|
||||||
|
|
||||||
|
{
|
||||||
|
stack.push(EMPTY_DOCUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonStructWriter(Writer out) {
|
||||||
|
Objects.requireNonNull(out);
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeBeforeValue(ThriftType type) throws IOException {
|
||||||
|
writeBegin(EMPTY_STRUCT, '{');
|
||||||
|
name("type");
|
||||||
|
beforeValue();
|
||||||
|
writeString(type.toString());
|
||||||
|
name("value");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeAfterValue() throws IOException {
|
||||||
|
writeEnd(EMPTY_STRUCT, NONEMPTY_STRUCT, '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newline() throws IOException {
|
||||||
|
out.write("\n");
|
||||||
|
for (int i = 0; i < stack.size(); i++) {
|
||||||
|
out.write(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void beforeValue() throws IOException {
|
||||||
|
switch (stack.peek()) {
|
||||||
|
case EMPTY_LIST:
|
||||||
|
stack.pop();
|
||||||
|
stack.push(NONEMPTY_LIST);
|
||||||
|
newline();
|
||||||
|
break;
|
||||||
|
case EMPTY_MAP:
|
||||||
|
stack.pop();
|
||||||
|
stack.push(NONEMPTY_MAP);
|
||||||
|
newline();
|
||||||
|
break;
|
||||||
|
case NONEMPTY_LIST:
|
||||||
|
out.append(',');
|
||||||
|
newline();
|
||||||
|
break;
|
||||||
|
case NONEMPTY_MAP:
|
||||||
|
out.append(',');
|
||||||
|
newline();
|
||||||
|
break;
|
||||||
|
case EMPTY_DOCUMENT:
|
||||||
|
stack.pop();
|
||||||
|
stack.push(NONEMPTY_DOCUMENT);
|
||||||
|
break;
|
||||||
|
case JSON_NAME:
|
||||||
|
out.append(':');
|
||||||
|
stack.pop();
|
||||||
|
stack.push(NONEMPTY_STRUCT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeString(String value) throws IOException {
|
||||||
|
out.write('"');
|
||||||
|
out.write(value);
|
||||||
|
out.write('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeBegin(byte empty, char symbol) throws IOException {
|
||||||
|
beforeValue();
|
||||||
|
stack.push(empty);
|
||||||
|
out.write(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeEnd(byte empty, byte nonEmpty, char symbol) throws IOException {
|
||||||
|
byte element = stack.peek();
|
||||||
|
if (element != empty && element != nonEmpty) {
|
||||||
|
throw new BadFormatException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element == nonEmpty) {
|
||||||
|
newline();
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
out.write(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeValue(String value, ThriftType type) throws IOException {
|
||||||
|
writeBeforeValue(type);
|
||||||
|
beforeValue();
|
||||||
|
if (type == ThriftType.BINARY || type == ThriftType.STRING) {
|
||||||
|
writeString(value);
|
||||||
|
} else {
|
||||||
|
out.write(value);
|
||||||
|
}
|
||||||
|
writeAfterValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginStruct() throws IOException {
|
public void beginStruct() throws IOException {
|
||||||
|
writeBeforeValue(ThriftType.STRUCT);
|
||||||
|
writeBegin(EMPTY_STRUCT, '{');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endStruct() throws IOException {
|
public void endStruct() throws IOException {
|
||||||
|
writeEnd(EMPTY_STRUCT, NONEMPTY_STRUCT, '}');
|
||||||
|
writeAfterValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginList(int size) throws IOException {
|
public void beginList(int size) throws IOException {
|
||||||
|
writeBeforeValue(ThriftType.LIST);
|
||||||
|
writeBegin(EMPTY_LIST, '[');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endList() throws IOException {
|
public void endList() throws IOException {
|
||||||
|
writeEnd(EMPTY_LIST, NONEMPTY_LIST, ']');
|
||||||
|
writeAfterValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginMap(int size) throws IOException {
|
public void beginMap(int size) throws IOException {
|
||||||
|
writeBeforeValue(ThriftType.MAP);
|
||||||
|
writeBegin(EMPTY_MAP, '[');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endMap() throws IOException {
|
public void endMap() throws IOException {
|
||||||
|
writeEnd(EMPTY_MAP, NONEMPTY_MAP, ']');
|
||||||
|
writeAfterValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginKey() throws IOException {
|
public void beginKey() throws IOException {
|
||||||
|
writeBegin(EMPTY_STRUCT, '{');
|
||||||
|
name("key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endKey() throws IOException {
|
public void endKey() throws IOException {
|
||||||
|
if (stack.peek() == JSON_NAME) {
|
||||||
|
throw new BadFormatException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginValue() throws IOException {
|
public void beginValue() throws IOException {
|
||||||
|
name("value");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endValue() throws IOException {
|
public void endValue() throws IOException {
|
||||||
|
writeEnd(EMPTY_STRUCT, NONEMPTY_STRUCT, '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void name(String name) throws IOException {
|
public void name(String name) throws IOException {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
|
|
||||||
|
if (stack.peek() == NONEMPTY_STRUCT) {
|
||||||
|
out.write(',');
|
||||||
|
}
|
||||||
|
newline();
|
||||||
|
stack.pop();
|
||||||
|
stack.push(JSON_NAME);
|
||||||
|
|
||||||
|
writeString(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(boolean value) throws IOException {
|
public void value(boolean value) throws IOException {
|
||||||
|
writeValue(value ? "true" : "false", ThriftType.BOOLEAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(String value) throws IOException {
|
public void value(String value) throws IOException {
|
||||||
|
writeValue(value, ThriftType.STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(byte value) throws IOException {
|
public void value(byte value) throws IOException {
|
||||||
|
writeValue(Byte.toString(value), ThriftType.BYTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(short value) throws IOException {
|
public void value(short value) throws IOException {
|
||||||
|
writeValue(Short.toString(value), ThriftType.SHORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(int value) throws IOException {
|
public void value(int value) throws IOException {
|
||||||
|
writeValue(Integer.toString(value), ThriftType.INTEGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(double value) throws IOException {
|
public void value(double value) throws IOException {
|
||||||
|
writeValue(Double.toString(value), ThriftType.DOUBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(long value) throws IOException {
|
public void value(long value) throws IOException {
|
||||||
|
writeValue(Long.toString(value), ThriftType.LONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void value(byte[] value) throws IOException {
|
public void value(byte[] value) throws IOException {
|
||||||
|
writeValue(Base64.getEncoder().encodeToString(value), ThriftType.BINARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nullValue() throws IOException {
|
public void nullValue() throws IOException {
|
||||||
|
writeValue("null", ThriftType.NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
if (stack.size() > 1 || stack.size() == 1 && stack.peek() == NONEMPTY_DOCUMENT) {
|
||||||
|
throw new BadFormatException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,19 +36,17 @@ public class KebabTest {
|
|||||||
testObject.setFuck(Arrays.asList(suk, suk, suk));
|
testObject.setFuck(Arrays.asList(suk, suk, suk));
|
||||||
|
|
||||||
Fail fail = new Fail();
|
Fail fail = new Fail();
|
||||||
|
|
||||||
fail.setReasons(new HashSet<>(Arrays.asList("kek1", "kek2")));
|
fail.setReasons(new HashSet<>(Arrays.asList("kek1", "kek2")));
|
||||||
|
testObject.setStatus(Status.fail(new Fail(fail)));
|
||||||
Map<String, Integer> map = new HashMap<>();
|
Map<String, Integer> map = new HashMap<>();
|
||||||
map.put("kek1", 455);
|
map.put("kek1", 455);
|
||||||
map.put("kek2", 564);
|
map.put("kek2", 564);
|
||||||
map.put("kek3", 565);
|
map.put("kek3", 565);
|
||||||
|
map.put(null, null);
|
||||||
testObject.setMaps(map);
|
testObject.setMaps(map);
|
||||||
|
|
||||||
testObject.setStatus(Status.fail(new Fail(fail)));
|
|
||||||
|
|
||||||
Kebab kebab = new Kebab();
|
Kebab kebab = new Kebab();
|
||||||
kebab.toJson(testObject);
|
System.out.println(kebab.toJson(testObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user