If you are using Jackson for JSON parsing, it can automatically convert byte[] to/from Base64 encoded Strings via data-binding.
Or, if you want low-level access, both JsonParser and JsonGenerator have binary access methods (writeBinary, readBinary) to do the same at level of JSON token stream.
For automatic approach, consider POJO like:
public class Message {
public String mimetype;
public byte[] value;
}
and to create JSON, you could do:
Message msg = ...;
String jsonStr = new ObjectMapper().writeValueAsString(msg);
or, more commonly would write it out with:
OutputStream out = ...;
new ObjectMapper().writeValue(out, msg);
Answer from StaxMan on Stack OverflowIf you are using Jackson for JSON parsing, it can automatically convert byte[] to/from Base64 encoded Strings via data-binding.
Or, if you want low-level access, both JsonParser and JsonGenerator have binary access methods (writeBinary, readBinary) to do the same at level of JSON token stream.
For automatic approach, consider POJO like:
public class Message {
public String mimetype;
public byte[] value;
}
and to create JSON, you could do:
Message msg = ...;
String jsonStr = new ObjectMapper().writeValueAsString(msg);
or, more commonly would write it out with:
OutputStream out = ...;
new ObjectMapper().writeValue(out, msg);
You can write your own CustomSerializer like this one:
public class ByteArraySerializer extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] bytes, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartArray();
for (byte b : bytes) {
jgen.writeNumber(unsignedToBytes(b));
}
jgen.writeEndArray();
}
private static int unsignedToBytes(byte b) {
return b & 0xFF;
}
}
This one returns an unsigned byte array representation instead of a Base64 string.
How to use it with your POJO:
public class YourPojo {
@JsonProperty("mimetype")
private String mimetype;
@JsonProperty("value")
private byte[] value;
public String getMimetype() { return this.mimetype; }
public void setMimetype(String mimetype) { this.mimetype = mimetype; }
@JsonSerialize(using= com.example.yourapp.ByteArraySerializer.class)
public byte[] getValue() { return this.value; }
public void setValue(String value) { this.value = value; }
}
And here is an example of it's output:
{
"mimetype": "text/plain",
"value": [
81,
109,
70,
122,
90,
83,
65,
50,
78,
67,
66,
84,
100,
72,
74,
108,
89,
87,
48,
61
]
}
P.S.: This serializer is a mix of some answers that I found on StackOverflow.
java - Current recommended way to convert byte[] to JsonNode and back - Stack Overflow
convert JSON Type to Byte array format in java - Stack Overflow
java - Put byte array to JSON and vice versa - Stack Overflow
java - how to deserialize / serialize byte array using Jackson and wrapper object - Stack Overflow
Here is a good example of base64 encoding byte arrays. It gets more complicated when you throw unicode characters in the mix to send things like PDF documents. After encoding a byte array the encoded string can be used as a JSON property value.
Apache commons offers good utilities:
byte[] bytes = getByteArr();
String base64String = Base64.encodeBase64String(bytes);
byte[] backToBytes = Base64.decodeBase64(base64String);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
Java server side example:
public String getUnsecureContentBase64(String url)
throws ClientProtocolException, IOException {
//getUnsecureContent will generate some byte[]
byte[] result = getUnsecureContent(url);
// use apache org.apache.commons.codec.binary.Base64
// if you're sending back as a http request result you may have to
// org.apache.commons.httpclient.util.URIUtil.encodeQuery
return Base64.encodeBase64String(result);
}
JavaScript decode:
//decode URL encoding if encoded before returning result
var uriEncodedString = decodeURIComponent(response);
var byteArr = base64DecToArr(uriEncodedString);
//from mozilla
function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ?
nChr - 65
: nChr > 96 && nChr < 123 ?
nChr - 71
: nChr > 47 && nChr < 58 ?
nChr + 4
: nChr === 43 ?
62
: nChr === 47 ?
63
:
0;
}
function base64DecToArr (sBase64, nBlocksSize) {
var
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
}
nUint24 = 0;
}
}
return taBytes;
}
The typical way to send binary in json is to base64 encode it.
Java provides different ways to Base64 encode and decode a byte[]. One of these is DatatypeConverter.
Very simply
byte[] originalBytes = new byte[] { 1, 2, 3, 4, 5};
String base64Encoded = DatatypeConverter.printBase64Binary(originalBytes);
byte[] base64Decoded = DatatypeConverter.parseBase64Binary(base64Encoded);
You'll have to make this conversion depending on the json parser/generator library you use.
This can be done by writing your own custom serializer and deserializer.
http://www.baeldung.com/jackson-custom-serialization
http://www.baeldung.com/jackson-deserialization
Here's an example similar to what you are doing...
Holder class
public static class Holder {
private Bytes bytes;
private String otherStuff;
public Bytes getBytes() {
return bytes;
}
public void setBytes(Bytes bytes) {
this.bytes = bytes;
}
public String getOtherStuff() {
return otherStuff;
}
public void setOtherStuff(String otherStuff) {
this.otherStuff = otherStuff;
}
}
Bytes class
Notice the annotations for the custom serializer...
@JsonSerialize(using = BytesSerializer.class)
@JsonDeserialize(using = BytesDeserializer.class)
public static class Bytes {
private byte[] bytes;
public Bytes(byte[] bytes) {
this.bytes = bytes;
}
public byte[] getBytes() {
return bytes;
}
}
Serializer
This will serialize a "Bytes" object as a base64 string...
public static class BytesSerializer extends StdSerializer<Bytes> {
private static final long serialVersionUID = -5510353102817291511L;
public BytesSerializer() {
super(Bytes.class);
}
@Override
public void serialize(Bytes value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(Base64.encode(value.getBytes()));
}
}
Deserializer
public static class BytesDeserializer extends StdDeserializer<Bytes> {
private static final long serialVersionUID = 1514703510863497028L;
public BytesDeserializer() {
super(Bytes.class);
}
@Override
public Bytes deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String base64 = node.asText();
return new Bytes(Base64.decode(base64));
}
}
Main method
A simple test method...
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Holder holder = new Holder();
holder.setOtherStuff("[OTHER STUFF]");
holder.setBytes(new Bytes(new byte[] { 1, 2, 3, 4, 5 }));
String json = mapper.writeValueAsString(holder);
System.out.println(json);
Holder deserialised = mapper.readValue(json, Holder.class);
System.out.println(Arrays.toString(deserialised.getBytes().getBytes()));
}
Output
{"bytes":"AQIDBAU=","otherStuff":"[OTHER STUFF]"}
[1, 2, 3, 4, 5]
Run it yourself
All of the classes above are "static" because I'd wrapped them into one big class called "Stack".
If you want to run this, create a new class (called anything you want) and paste all the code here into it...
I could not use a wrapped object, but I was able to figure out it wasn't necessary; the following works for me:
public static class ByteArraySerializer extends StdSerializer<byte[]> {
public ByteArraySerializer() {
super(byte[].class);
}
@Override
public void serialize(byte[] value, JsonGenerator gen,SerializerProvider provider) throws IOException {
gen.writeString(Base64.getEncoder().encodeToString(value));
}
}
public static class ByteArrayDeserializer extends StdDeserializer<byte[]> {
private static final long serialVersionUID = 1514703510863497028L;
public ByteArrayDeserializer() {
super(byte[].class);
}
@Override
public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);
String base64 = node.asText();
return Base64.getDecoder().decode(base64);
}
}
public ObjectMapper getJsonMapper() {
ObjectMapper jsonMapper = new ObjectMapper();
SimpleModule module = new SimpleModule("ByteArraySerializer", new Version(1, 0, 0, ""));
module.addSerializer(byte[].class, new ByteArraySerializer());
module.addDeserializer(byte[].class, new ByteArrayDeserializer());
jsonMapper.registerModule(module);
}
Jackson will convert byte[] into Base64-encoded binary data. This is the safe way to pass binary content. Otherwise there is no way to know which character encoding might be used by contained data, so trying to build a String out of it would be risky and error-prone.
So the simplest way would be to have receiver base64 decode contents back into binary data.
You could alternatively add custom serializer to turn into other representations (hex, base85), but it really depends on what the goal is.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is how you could support this use case with MOXy as your JSON-binding provider.
Java Model
By default a JAXB implementation will convert a byte[] to base64Binary. You can use the HexBinaryAdapter to have it represented as hexBinary.
package forum15643723;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private byte[] foo;
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
private byte[] bar;
}
Demo
In the demo code below we will read the JSON into objects and then write it back to JSON.
package forum15643723;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Root.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum15643723/input.json");
Root root = (Root) unmarshaller.unmarshal(json, Root.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.json/Output
The foo and bar properties represent the same data. foo is represented as base64Binary and bar is represented as hexBinary.
{
"foo" : "3q2+78r+",
"bar" : "DEADBEEFCAFE"
}
For More Information
- http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
Since you are not specifying no CharSet on converting the Json array string to bytes. Simply use :
arr = new JSONArray(new String(bArr));
The typical way to send binary in json is to base64 encode it. Java provides different ways to Base64 encode and decode a byte[]. One of these is DatatypeConverter.
Very simply
byte[] originalBytes = new byte[] { 1, 2, 3, 4, 5};
String base64Encoded = DatatypeConverter.printBase64Binary(originalBytes);
byte[] base64Decoded = DatatypeConverter.parseBase64Binary(base64Encoded);