Add SimpleJsonProtocol
This commit is contained in:
Родитель
2f5080e38c
Коммит
3cc350ba63
|
@ -39,7 +39,8 @@ allprojects {
|
|||
|
||||
testing: [
|
||||
'junit:junit:4.12',
|
||||
'org.mockito:mockito-core:1.10.19'
|
||||
'org.mockito:mockito-core:1.10.19',
|
||||
'com.google.truth:truth:0.28'
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
|
@ -31,6 +31,5 @@ dependencies {
|
|||
|
||||
testCompile libraries.testing
|
||||
|
||||
testCompile 'com.google.truth:truth:0.28'
|
||||
testCompile 'com.google.testing.compile:compile-testing:0.9'
|
||||
}
|
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.protocol;
|
||||
|
||||
import com.microsoft.thrifty.transport.Transport;
|
||||
import com.microsoft.thrifty.util.UnsafeByteArrayOutputStream;
|
||||
import okio.ByteString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* A protocol that maps Thrift data to idiomatic JSON.
|
||||
*
|
||||
* <p>"Idiomatic" here means that structs map to JSON maps, with field names
|
||||
* for keys. Field tags are not included, and precise type information is not
|
||||
* preserved. For this reason, SimpleJsonProtocol <em>does not support round-
|
||||
* tripping</em> - it is write-only.
|
||||
*
|
||||
* <p>Note that, as of the initial release, this Protocol does not guarantee
|
||||
* that all emitted data is strictly valid JSON. In particular, map keys are
|
||||
* not guaranteed to to be strings.
|
||||
*/
|
||||
public class SimpleJsonProtocol extends Protocol {
|
||||
/**
|
||||
* Indicates how {@linkplain ByteString binary} data is serialized when
|
||||
* written as JSON.
|
||||
*/
|
||||
public enum BinaryOutputMode {
|
||||
/**
|
||||
* Write binary data as a hex-encoded string.
|
||||
*/
|
||||
HEX,
|
||||
|
||||
/**
|
||||
* Write binary data as a base-64-encoded string.
|
||||
*/
|
||||
BASE_64,
|
||||
|
||||
/**
|
||||
* Write binary data using Unicode escape syntax.
|
||||
*/
|
||||
UNICODE,
|
||||
}
|
||||
|
||||
private class WriteContext {
|
||||
void beforeWrite() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
void onPop() throws IOException {
|
||||
// Fine
|
||||
}
|
||||
}
|
||||
|
||||
private class ListWriteContext extends WriteContext {
|
||||
private boolean hasWritten = false;
|
||||
|
||||
@Override
|
||||
void beforeWrite() throws IOException {
|
||||
if (hasWritten) {
|
||||
transport.write(COMMA);
|
||||
} else {
|
||||
hasWritten = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MapWriteContext extends WriteContext {
|
||||
private static final boolean MODE_KEY = false;
|
||||
private static final boolean MODE_VALUE = true;
|
||||
|
||||
private boolean hasWritten = false;
|
||||
private boolean mode = MODE_KEY;
|
||||
|
||||
@Override
|
||||
void beforeWrite() throws IOException {
|
||||
if (hasWritten) {
|
||||
if (mode == MODE_KEY) {
|
||||
transport.write(COMMA);
|
||||
} else {
|
||||
transport.write(COLON);
|
||||
}
|
||||
} else {
|
||||
hasWritten = true;
|
||||
}
|
||||
|
||||
mode = !mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onPop() throws IOException {
|
||||
if (mode == MODE_VALUE) {
|
||||
throw new ProtocolException("Incomplete JSON map, expected a value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[][] ESCAPES;
|
||||
private static final Charset UTF8;
|
||||
|
||||
private static final byte[] TRUE = { 't', 'r', 'u', 'e' };
|
||||
private static final byte[] FALSE = { 'f', 'a', 'l', 's', 'e' };
|
||||
private static final byte[] COMMA = { ',' };
|
||||
private static final byte[] COMMA_SPACE = { ',', ' ' };
|
||||
private static final byte[] COLON = { ':' };
|
||||
private static final byte[] LBRACKET = { '[' };
|
||||
private static final byte[] RBRACKET = { ']' };
|
||||
private static final byte[] LBRACE = { '{' };
|
||||
private static final byte[] RBRACE = { '}' };
|
||||
|
||||
static {
|
||||
ESCAPES = new byte[128][];
|
||||
|
||||
UTF8 = Charset.forName("UTF-8");
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// Control chars must be escaped
|
||||
ESCAPES[i] = String.format("\\u%04x", i).getBytes(UTF8);
|
||||
}
|
||||
|
||||
ESCAPES['\\'] = new byte[] { '\\', '\\' };
|
||||
ESCAPES['\"'] = new byte[] { '\\', '"' };
|
||||
ESCAPES['\b'] = new byte[] { '\\', 'b' };
|
||||
ESCAPES['\f'] = new byte[] { '\\', 'f' };
|
||||
ESCAPES['\r'] = new byte[] { '\\', 'r' };
|
||||
ESCAPES['\n'] = new byte[] { '\\', 'n' };
|
||||
ESCAPES['\t'] = new byte[] { '\\', 't' };
|
||||
}
|
||||
|
||||
private final WriteContext defaultWriteContext = new WriteContext() {
|
||||
@Override
|
||||
void beforeWrite() throws IOException {
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
|
||||
private Deque<WriteContext> writeStack = new ArrayDeque<>();
|
||||
|
||||
private BinaryOutputMode binaryOutputMode = BinaryOutputMode.HEX;
|
||||
|
||||
public SimpleJsonProtocol(Transport transport) {
|
||||
super(transport);
|
||||
}
|
||||
|
||||
public SimpleJsonProtocol withBinaryOutputMode(BinaryOutputMode mode) {
|
||||
binaryOutputMode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMessageBegin(String name, byte typeId, int seqId) throws IOException {
|
||||
writeMapBegin(typeId, typeId, 0); // values are ignored here
|
||||
writeString("name");
|
||||
writeString(name);
|
||||
|
||||
writeString("value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMessageEnd() throws IOException {
|
||||
writeMapEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStructBegin(String structName) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
pushWriteContext(new MapWriteContext());
|
||||
transport.write(LBRACE);
|
||||
|
||||
writeString("__thriftStruct");
|
||||
writeString(structName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeStructEnd() throws IOException {
|
||||
transport.write(RBRACE);
|
||||
popWriteContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFieldBegin(String fieldName, int fieldId, byte typeId) throws IOException {
|
||||
// TODO: assert that we're in map context
|
||||
writeString(fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFieldEnd() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFieldStop() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMapBegin(byte keyTypeId, byte valueTypeId, int mapSize) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
pushWriteContext(new MapWriteContext());
|
||||
transport.write(LBRACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMapEnd() throws IOException {
|
||||
transport.write(RBRACE);
|
||||
popWriteContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListBegin(byte elementTypeId, int listSize) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
pushWriteContext(new ListWriteContext());
|
||||
transport.write(LBRACKET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeListEnd() throws IOException {
|
||||
transport.write(RBRACKET);
|
||||
popWriteContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSetBegin(byte elementTypeId, int setSize) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
pushWriteContext(new ListWriteContext());
|
||||
transport.write(LBRACKET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSetEnd() throws IOException {
|
||||
transport.write(RBRACKET);
|
||||
popWriteContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBool(boolean b) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
transport.write(b ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(byte b) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
byte[] toWrite = String.valueOf(b).getBytes(UTF8);
|
||||
transport.write(toWrite);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeI16(short i16) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
transport.write(String.valueOf(i16).getBytes(UTF8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeI32(int i32) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
transport.write(String.valueOf(i32).getBytes(UTF8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeI64(long i64) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
transport.write(String.valueOf(i64).getBytes(UTF8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double dub) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
transport.write(String.valueOf(dub).getBytes(UTF8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(String str) throws IOException {
|
||||
writeContext().beforeWrite();
|
||||
|
||||
int len = str.length();
|
||||
UnsafeByteArrayOutputStream baos = new UnsafeByteArrayOutputStream(len);
|
||||
|
||||
baos.write('"');
|
||||
for (int i = 0; i < len; ++i) {
|
||||
char c = str.charAt(i);
|
||||
if (c < 128) {
|
||||
byte[] maybeEscape = ESCAPES[c];
|
||||
if (maybeEscape != null) {
|
||||
baos.write(maybeEscape);
|
||||
} else {
|
||||
baos.write(c);
|
||||
}
|
||||
} else {
|
||||
baos.write(c);
|
||||
}
|
||||
}
|
||||
baos.write('"');
|
||||
|
||||
transport.write(baos.getBuffer(), 0, baos.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBinary(ByteString buf) throws IOException {
|
||||
String out;
|
||||
switch (binaryOutputMode) {
|
||||
case HEX: out = buf.hex(); break;
|
||||
case BASE_64: out = buf.base64(); break;
|
||||
case UNICODE: out = buf.utf8(); break;
|
||||
default:
|
||||
throw new AssertionError("Unexpected BinaryOutputMode value: " + binaryOutputMode);
|
||||
}
|
||||
writeString(out);
|
||||
}
|
||||
|
||||
private void pushWriteContext(WriteContext context) {
|
||||
writeStack.push(context);
|
||||
}
|
||||
|
||||
private WriteContext writeContext() {
|
||||
WriteContext top = writeStack.peek();
|
||||
if (top == null) {
|
||||
top = defaultWriteContext;
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
private void popWriteContext() throws IOException {
|
||||
WriteContext context = writeStack.pollFirst();
|
||||
if (context == null) {
|
||||
throw new ProtocolException("stack underflow");
|
||||
} else {
|
||||
context.onPop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageMetadata readMessageBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMessageEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructMetadata readStructBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readStructEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldMetadata readFieldBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFieldEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapMetadata readMapBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMapEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListMetadata readListBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readListEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetMetadata readSetBegin() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSetEnd() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBool() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readI16() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readI32() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readI64() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readString() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString readBinary() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
package com.microsoft.thrifty.transport;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import com.microsoft.thrifty.util.UnsafeByteArrayOutputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
|
@ -93,14 +94,4 @@ public class FramedTransport extends Transport {
|
|||
pendingWrite.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnsafeByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
public UnsafeByteArrayOutputStream(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* A {@link ByteArrayOutputStream} that exposes its internal backing byte
|
||||
* array. It is intended to reduce the number of memory copies made during
|
||||
* serialization. Use with extreme caution.
|
||||
*/
|
||||
public class UnsafeByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
public UnsafeByteArrayOutputStream(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return buf;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Thrifty
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
*
|
||||
* See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package com.microsoft.thrifty.protocol;
|
||||
|
||||
import com.microsoft.thrifty.TType;
|
||||
import com.microsoft.thrifty.transport.BufferTransport;
|
||||
import okio.Buffer;
|
||||
import okio.ByteString;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
public class SimpleJsonProtocolTest {
|
||||
private Buffer buffer;
|
||||
private BufferTransport transport;
|
||||
private SimpleJsonProtocol protocol;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
buffer = new Buffer();
|
||||
transport = new BufferTransport(buffer);
|
||||
protocol = new SimpleJsonProtocol(transport);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyJsonString() throws Exception {
|
||||
protocol.writeString("");
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesNamedControlChars() throws Exception {
|
||||
protocol.writeString("\b\f\r\n\t");
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"\\b\\f\\r\\n\\t\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapesQuotes() throws Exception {
|
||||
protocol.writeString("\"");
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"\\\"\""); // or, in other words, "\""
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalStringIsQuoted() throws Exception {
|
||||
protocol.writeString("y u no quote me?");
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"y u no quote me?\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyList() throws Exception {
|
||||
protocol.writeListBegin(TType.STRING, 0);
|
||||
protocol.writeListEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("[]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listWithOneElement() throws Exception {
|
||||
protocol.writeListBegin(TType.STRING, 0);
|
||||
protocol.writeString("foo");
|
||||
protocol.writeListEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("[\"foo\"]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listWithTwoElements() throws Exception {
|
||||
protocol.writeListBegin(TType.STRING, 0);
|
||||
protocol.writeString("foo");
|
||||
protocol.writeString("bar");
|
||||
protocol.writeListEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("[\"foo\",\"bar\"]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyMap() throws Exception {
|
||||
protocol.writeMapBegin(TType.STRING, TType.I32, 0);
|
||||
protocol.writeMapEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("{}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithSingleElement() throws Exception {
|
||||
protocol.writeMapBegin(TType.STRING, TType.I32, 0);
|
||||
protocol.writeString("key1");
|
||||
protocol.writeI32(1);
|
||||
protocol.writeMapEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("{\"key1\":1}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithTwoElements() throws Exception {
|
||||
protocol.writeMapBegin(TType.STRING, TType.I32, 0);
|
||||
protocol.writeString("key1");
|
||||
protocol.writeI32(1);
|
||||
protocol.writeString("key2");
|
||||
protocol.writeI32(2);
|
||||
protocol.writeMapEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("{\"key1\":1,\"key2\":2}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listOfMaps() throws Exception {
|
||||
protocol.writeListBegin(TType.MAP, 2);
|
||||
|
||||
protocol.writeMapBegin(TType.STRING, TType.I32, 1);
|
||||
protocol.writeString("1");
|
||||
protocol.writeI32(10);
|
||||
protocol.writeMapEnd();
|
||||
|
||||
protocol.writeMapBegin(TType.STRING, TType.I32, 1);
|
||||
protocol.writeString("2");
|
||||
protocol.writeI32(20);
|
||||
protocol.writeMapEnd();
|
||||
|
||||
protocol.writeListEnd();
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("[{\"1\":10},{\"2\":20}]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structs() throws Exception {
|
||||
Xtruct xtruct = new Xtruct.Builder()
|
||||
.byte_thing((byte) 1)
|
||||
.double_thing(2.0)
|
||||
.i32_thing(3)
|
||||
.i64_thing(4L)
|
||||
.string_thing("five")
|
||||
.build();
|
||||
|
||||
Xtruct.ADAPTER.write(protocol, xtruct);
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("" +
|
||||
"{\"__thriftStruct\":\"Xtruct\"," +
|
||||
"\"string_thing\":\"five\"," +
|
||||
"\"byte_thing\":1," +
|
||||
"\"i32_thing\":3," +
|
||||
"\"i64_thing\":4," +
|
||||
"\"double_thing\":2.0}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hexBinaryOutputMode() throws Exception {
|
||||
protocol.withBinaryOutputMode(SimpleJsonProtocol.BinaryOutputMode.HEX)
|
||||
.writeBinary(ByteString.of(new byte[] { 0, 127, -1 }));
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"007fff\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void b64BinaryOutputMode() throws Exception {
|
||||
protocol.withBinaryOutputMode(SimpleJsonProtocol.BinaryOutputMode.BASE_64)
|
||||
.writeBinary(ByteString.encodeUtf8("foobar"));
|
||||
|
||||
assertThat(buffer.readUtf8()).isEqualTo("\"Zm9vYmFy\"");
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче