Skip to content

Commit 887722d

Browse files
committed
Fix #284
1 parent d2080f6 commit 887722d

File tree

6 files changed

+209
-18
lines changed

6 files changed

+209
-18
lines changed

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,11 +1699,13 @@ public int getText(Writer writer) throws IOException
16991699
@Override
17001700
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
17011701
{
1702-
if (_tokenIncomplete) {
1703-
_finishToken();
1704-
}
1705-
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT ) {
1706-
// TODO, maybe: support base64 for text?
1702+
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
1703+
if (_tokenIncomplete) {
1704+
_finishToken();
1705+
}
1706+
} else if (_currToken == JsonToken.VALUE_STRING) {
1707+
return _getBinaryFromString(b64variant);
1708+
} else {
17071709
_reportError("Current token ("+currentToken()+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
17081710
}
17091711
return _binaryValue;
@@ -1724,8 +1726,15 @@ public Object getEmbeddedObject() throws IOException
17241726
@Override
17251727
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
17261728
{
1727-
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT ) {
1728-
// Todo, maybe: support base64 for text?
1729+
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT) {
1730+
if (_currToken == JsonToken.VALUE_STRING) {
1731+
// 26-Jun-2021, tatu: Not optimized; could make streaming if we
1732+
// really want in future
1733+
final byte[] b = _getBinaryFromString(b64variant);
1734+
final int len = b.length;
1735+
out.write(b, 0, len);
1736+
return len;
1737+
}
17291738
_reportError("Current token ("+currentToken()+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
17301739
}
17311740
if (!_tokenIncomplete) { // someone already decoded or read
@@ -1773,6 +1782,21 @@ private int _readAndWriteBytes(OutputStream out, final int total) throws IOExcep
17731782
return total;
17741783
}
17751784

1785+
// @since 2.13
1786+
private final byte[] _getBinaryFromString(Base64Variant variant) throws IOException
1787+
{
1788+
if (_tokenIncomplete) {
1789+
_finishToken();
1790+
}
1791+
if (_binaryValue == null) {
1792+
// 26-Jun-2021, tatu: Copied from ParserBase
1793+
ByteArrayBuilder builder = _getByteArrayBuilder();
1794+
_decodeBase64(getText(), builder, variant);
1795+
_binaryValue = builder.toByteArray();
1796+
}
1797+
return _binaryValue;
1798+
}
1799+
17761800
/*
17771801
/**********************************************************
17781802
/* Numeric accessors of public API
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.fasterxml.jackson.dataformat.cbor.parse;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.nio.charset.StandardCharsets;
5+
6+
import org.junit.Assert;
7+
8+
import com.fasterxml.jackson.core.JsonParser;
9+
import com.fasterxml.jackson.core.JsonToken;
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.fasterxml.jackson.dataformat.cbor.CBORTestBase;
12+
13+
public class Base64AsBinaryTest extends CBORTestBase
14+
{
15+
private final static String ENCODED_BASE64 = "VGVzdCE=";
16+
private final static byte[] DECODED_BASE64 = "Test!".getBytes(StandardCharsets.US_ASCII);
17+
18+
private final byte[] CBOR_DOC;
19+
20+
private final ObjectMapper MAPPER = cborMapper();
21+
22+
public Base64AsBinaryTest() throws Exception {
23+
CBOR_DOC = cborDoc("{\"value\":\""+ENCODED_BASE64+"\"}");
24+
}
25+
26+
// [dataformats-binary#284]: binary from Base64 encoded
27+
public void testGetBase64AsBinary() throws Exception
28+
{
29+
// First, verify regularly
30+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
31+
assertToken(JsonToken.START_OBJECT, p.nextToken());
32+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
33+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
34+
assertEquals(ENCODED_BASE64, p.getText());
35+
assertToken(JsonToken.END_OBJECT, p.nextToken());
36+
assertNull(p.nextToken());
37+
}
38+
39+
// then with getBinaryValue()
40+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
41+
assertToken(JsonToken.START_OBJECT, p.nextToken());
42+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
43+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
44+
byte[] binary = p.getBinaryValue();
45+
Assert.assertArrayEquals(DECODED_BASE64, binary);
46+
assertToken(JsonToken.END_OBJECT, p.nextToken());
47+
assertNull(p.nextToken());
48+
}
49+
}
50+
51+
// [dataformats-binary#284]: binary from Base64 encoded
52+
public void testReadBase64AsBinary() throws Exception
53+
{
54+
// And further via read
55+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
56+
assertToken(JsonToken.START_OBJECT, p.nextToken());
57+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
58+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
59+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
60+
int count = p.readBinaryValue(bytes);
61+
assertEquals(5, count);
62+
Assert.assertArrayEquals(DECODED_BASE64, bytes.toByteArray());
63+
assertToken(JsonToken.END_OBJECT, p.nextToken());
64+
assertNull(p.nextToken());
65+
}
66+
}
67+
}

release-notes/CREDITS-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,8 @@ Nick (manaigrn-amzn@github)
199199
* Contributed #270: (ion) Ion Polymorphic deserialization in 2.12 breaks wrt use of
200200
Native Type Ids when upgrading from 2.8
201201
(2.12.3)
202+
203+
Hunter Herman (hherman1@github)
204+
205+
* Requested #284: Support base64 strings in `getBinaryValue()` for CBOR and Smile
206+
(2.13.0)

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Modules:
2323
#276: (smile) Add `SmileGenerator.Feature.LENIENT_UTF_ENCODING` for lenient handling
2424
of broken Unicode surrogate pairs on writing
2525
(requested by kireet@github)
26+
#284: Support base64 strings in `getBinaryValue()` for CBOR and Smile
27+
(requested by Hunter H)
2628
- `Ion-java` dep 1.4.0 -> 1.8.0
2729
- Minor change to Ion module registration names (fully-qualified)
2830

smile/src/main/java/com/fasterxml/jackson/dataformat/smile/SmileParser.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,12 +1204,14 @@ public int getText(Writer writer) throws IOException
12041204
@Override
12051205
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
12061206
{
1207-
if (_tokenIncomplete) {
1208-
_finishToken();
1209-
}
1210-
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT) {
1211-
// Todo, maybe: support base64 for text?
1212-
_reportError("Current token ("+_currToken+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
1207+
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
1208+
if (_tokenIncomplete) {
1209+
_finishToken();
1210+
}
1211+
} else if (_currToken == JsonToken.VALUE_STRING) {
1212+
return _getBinaryFromString(b64variant);
1213+
} else {
1214+
_reportError("Current token ("+currentToken()+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
12131215
}
12141216
return _binaryValue;
12151217
}
@@ -1220,7 +1222,7 @@ public Object getEmbeddedObject() throws IOException
12201222
if (_tokenIncomplete) {
12211223
_finishToken();
12221224
}
1223-
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
1225+
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
12241226
return _binaryValue;
12251227
}
12261228
return null;
@@ -1229,9 +1231,16 @@ public Object getEmbeddedObject() throws IOException
12291231
@Override
12301232
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
12311233
{
1232-
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT ) {
1233-
// Todo, maybe: support base64 for text?
1234-
_reportError("Current token ("+_currToken+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
1234+
if (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT) {
1235+
if (_currToken == JsonToken.VALUE_STRING) {
1236+
// 26-Jun-2021, tatu: Not optimized; could make streaming if we
1237+
// really want in future
1238+
final byte[] b = _getBinaryFromString(b64variant);
1239+
final int len = b.length;
1240+
out.write(b, 0, len);
1241+
return len;
1242+
}
1243+
_reportError("Current token ("+currentToken()+") not VALUE_EMBEDDED_OBJECT, can not access as binary");
12351244
}
12361245
// Ok, first, unlikely (but legal?) case where someone already requested binary data:
12371246
if (!_tokenIncomplete) {
@@ -1329,7 +1338,22 @@ private void _readBinaryEncoded(OutputStream out, int length, byte[] buffer) thr
13291338
out.write(buffer, 0, outPtr);
13301339
}
13311340
}
1332-
1341+
1342+
// @since 2.13
1343+
private final byte[] _getBinaryFromString(Base64Variant variant) throws IOException
1344+
{
1345+
if (_tokenIncomplete) {
1346+
_finishToken();
1347+
}
1348+
if (_binaryValue == null) {
1349+
// 26-Jun-2021, tatu: Copied from ParserBase (except no recycling of BAB here)
1350+
ByteArrayBuilder builder = new ByteArrayBuilder();
1351+
_decodeBase64(getText(), builder, variant);
1352+
_binaryValue = builder.toByteArray();
1353+
}
1354+
return _binaryValue;
1355+
}
1356+
13331357
/*
13341358
/**********************************************************
13351359
/* Internal methods, field name parsing
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.fasterxml.jackson.dataformat.smile.parse;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.nio.charset.StandardCharsets;
5+
6+
import org.junit.Assert;
7+
8+
import com.fasterxml.jackson.core.JsonParser;
9+
import com.fasterxml.jackson.core.JsonToken;
10+
11+
import com.fasterxml.jackson.databind.ObjectMapper;
12+
13+
import com.fasterxml.jackson.dataformat.smile.BaseTestForSmile;
14+
15+
public class Base64AsBinaryTest extends BaseTestForSmile
16+
{
17+
private final static String ENCODED_BASE64 = "VGVzdCE=";
18+
private final static byte[] DECODED_BASE64 = "Test!".getBytes(StandardCharsets.US_ASCII);
19+
20+
private final byte[] CBOR_DOC;
21+
22+
private final ObjectMapper MAPPER = smileMapper();
23+
24+
public Base64AsBinaryTest() throws Exception {
25+
CBOR_DOC = _smileDoc("{\"value\":\""+ENCODED_BASE64+"\"}");
26+
}
27+
28+
// [dataformats-binary#284]: binary from Base64 encoded
29+
public void testGetBase64AsBinary() throws Exception
30+
{
31+
// First, verify regularly
32+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
33+
assertToken(JsonToken.START_OBJECT, p.nextToken());
34+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
35+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
36+
assertEquals(ENCODED_BASE64, p.getText());
37+
assertToken(JsonToken.END_OBJECT, p.nextToken());
38+
assertNull(p.nextToken());
39+
}
40+
41+
// then with getBinaryValue()
42+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
43+
assertToken(JsonToken.START_OBJECT, p.nextToken());
44+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
45+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
46+
byte[] binary = p.getBinaryValue();
47+
Assert.assertArrayEquals(DECODED_BASE64, binary);
48+
assertToken(JsonToken.END_OBJECT, p.nextToken());
49+
assertNull(p.nextToken());
50+
}
51+
}
52+
53+
// [dataformats-binary#284]: binary from Base64 encoded
54+
public void testReadBase64AsBinary() throws Exception
55+
{
56+
// And further via read
57+
try (JsonParser p = MAPPER.createParser(CBOR_DOC)) {
58+
assertToken(JsonToken.START_OBJECT, p.nextToken());
59+
assertToken(JsonToken.FIELD_NAME, p.nextToken());
60+
assertToken(JsonToken.VALUE_STRING, p.nextToken());
61+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
62+
int count = p.readBinaryValue(bytes);
63+
assertEquals(5, count);
64+
Assert.assertArrayEquals(DECODED_BASE64, bytes.toByteArray());
65+
assertToken(JsonToken.END_OBJECT, p.nextToken());
66+
assertNull(p.nextToken());
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)