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:
Pavel Popov 2017-02-02 18:13:00 +04:00 committed by GitHub
parent ebbc8bacfd
commit a0a4fd0dba
5 changed files with 212 additions and 37 deletions

View File

@ -1,14 +1,26 @@
package com.rbkmoney.kebab;
import com.rbkmoney.kebab.serializer.TBaseSerializer;
import com.rbkmoney.kebab.writer.JsonStructWriter;
import org.apache.thrift.TBase;
import java.io.IOException;
import java.io.StringWriter;
/**
* Created by tolkonepiu on 24/01/2017.
*/
public class Kebab<T extends TBase> {
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) {

View File

@ -9,6 +9,7 @@ import java.util.Arrays;
*/
public enum ThriftType {
NULL(-1),
BOOLEAN(TType.BOOL),
BYTE(TType.BYTE),
DOUBLE(TType.DOUBLE),
@ -20,7 +21,8 @@ public enum ThriftType {
LIST(TType.LIST),
SET(TType.SET),
MAP(TType.MAP),
STRUCT(TType.STRUCT);
STRUCT(TType.STRUCT),
BINARY(21);
int code;

View File

@ -5,6 +5,7 @@ import com.rbkmoney.kebab.StructWriter;
import com.rbkmoney.kebab.ThriftType;
import org.apache.thrift.TBase;
import org.apache.thrift.TFieldIdEnum;
import org.apache.thrift.TFieldRequirementType;
import org.apache.thrift.meta_data.*;
import java.io.IOException;
@ -19,26 +20,38 @@ public class TBaseSerializer implements Serializer<TBase> {
@Override
public void write(StructWriter out, TBase value) throws IOException {
out.beginStruct();
if (value == null) {
out.nullValue();
return;
}
writeStruct(out, value);
}
private void writeStruct(StructWriter out, TBase value) throws IOException {
out.beginStruct();
TFieldIdEnum[] tFieldIdEnums = value.getFields();
Map<TFieldIdEnum, FieldMetaData> fieldMetaDataMap = value.getFieldMetaData();
for (TFieldIdEnum tFieldIdEnum : tFieldIdEnums) {
FieldMetaData fieldMetaData = fieldMetaDataMap.get(tFieldIdEnum);
if (value.isSet(tFieldIdEnum)) {
out.name(tFieldIdEnum.getFieldName());
FieldMetaData fieldMetaData = fieldMetaDataMap.get(tFieldIdEnum);
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();
}
private void write(StructWriter out, Object object, FieldValueMetaData fieldValueMetaData) throws IOException {
if (object == null) {
out.nullValue();
return;
}
ThriftType type = ThriftType.findByCode(fieldValueMetaData.getType());
boolean isBinary = fieldValueMetaData.isBinary();
@ -67,17 +80,20 @@ public class TBaseSerializer implements Serializer<TBase> {
case DOUBLE:
out.value((double) object);
break;
case ENUM:
out.value(object.toString());
break;
case LIST:
write(out, (List) object, (ListMetaData) fieldValueMetaData);
writeList(out, (List) object, (ListMetaData) fieldValueMetaData);
break;
case SET:
write(out, (Set) object, (SetMetaData) fieldValueMetaData);
writeSet(out, (Set) object, (SetMetaData) fieldValueMetaData);
break;
case MAP:
write(out, (Map) object, (MapMetaData) fieldValueMetaData);
writeMap(out, (Map) object, (MapMetaData) fieldValueMetaData);
break;
case STRUCT:
write(out, (TBase) object);
writeStruct(out, (TBase) object);
break;
default:
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());
for (Object object : objectSet) {
write(out, object, metaData.getElementMetaData());
@ -93,7 +109,7 @@ public class TBaseSerializer implements Serializer<TBase> {
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());
for (Object object : objectList) {
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());
for (Map.Entry entry : (Set<Map.Entry>) objectMap.entrySet()) {
out.beginKey();

View File

@ -1,116 +1,263 @@
package com.rbkmoney.kebab.writer;
import com.rbkmoney.kebab.ByteStack;
import com.rbkmoney.kebab.StructWriter;
import com.rbkmoney.kebab.ThriftType;
import com.rbkmoney.kebab.exception.BadFormatException;
import java.io.IOException;
import java.io.Writer;
import java.util.Base64;
import java.util.Objects;
/**
* Created by tolkonepiu on 27/01/2017.
*/
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
public void beginStruct() throws IOException {
writeBeforeValue(ThriftType.STRUCT);
writeBegin(EMPTY_STRUCT, '{');
}
@Override
public void endStruct() throws IOException {
writeEnd(EMPTY_STRUCT, NONEMPTY_STRUCT, '}');
writeAfterValue();
}
@Override
public void beginList(int size) throws IOException {
writeBeforeValue(ThriftType.LIST);
writeBegin(EMPTY_LIST, '[');
}
@Override
public void endList() throws IOException {
writeEnd(EMPTY_LIST, NONEMPTY_LIST, ']');
writeAfterValue();
}
@Override
public void beginMap(int size) throws IOException {
writeBeforeValue(ThriftType.MAP);
writeBegin(EMPTY_MAP, '[');
}
@Override
public void endMap() throws IOException {
writeEnd(EMPTY_MAP, NONEMPTY_MAP, ']');
writeAfterValue();
}
@Override
public void beginKey() throws IOException {
writeBegin(EMPTY_STRUCT, '{');
name("key");
}
@Override
public void endKey() throws IOException {
if (stack.peek() == JSON_NAME) {
throw new BadFormatException();
}
}
@Override
public void beginValue() throws IOException {
name("value");
}
@Override
public void endValue() throws IOException {
writeEnd(EMPTY_STRUCT, NONEMPTY_STRUCT, '}');
}
@Override
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
public void value(boolean value) throws IOException {
writeValue(value ? "true" : "false", ThriftType.BOOLEAN);
}
@Override
public void value(String value) throws IOException {
writeValue(value, ThriftType.STRING);
}
@Override
public void value(byte value) throws IOException {
writeValue(Byte.toString(value), ThriftType.BYTE);
}
@Override
public void value(short value) throws IOException {
writeValue(Short.toString(value), ThriftType.SHORT);
}
@Override
public void value(int value) throws IOException {
writeValue(Integer.toString(value), ThriftType.INTEGER);
}
@Override
public void value(double value) throws IOException {
writeValue(Double.toString(value), ThriftType.DOUBLE);
}
@Override
public void value(long value) throws IOException {
writeValue(Long.toString(value), ThriftType.LONG);
}
@Override
public void value(byte[] value) throws IOException {
writeValue(Base64.getEncoder().encodeToString(value), ThriftType.BINARY);
}
@Override
public void nullValue() throws IOException {
writeValue("null", ThriftType.NULL);
}
@Override
public void close() throws IOException {
out.close();
if (stack.size() > 1 || stack.size() == 1 && stack.peek() == NONEMPTY_DOCUMENT) {
throw new BadFormatException();
}
}
}

View File

@ -36,19 +36,17 @@ public class KebabTest {
testObject.setFuck(Arrays.asList(suk, suk, suk));
Fail fail = new Fail();
fail.setReasons(new HashSet<>(Arrays.asList("kek1", "kek2")));
testObject.setStatus(Status.fail(new Fail(fail)));
Map<String, Integer> map = new HashMap<>();
map.put("kek1", 455);
map.put("kek2", 564);
map.put("kek3", 565);
map.put(null, null);
testObject.setMaps(map);
testObject.setStatus(Status.fail(new Fail(fail)));
Kebab kebab = new Kebab();
kebab.toJson(testObject);
System.out.println(kebab.toJson(testObject));
}
}