Map key converted to byte array is not serialized as base64 string
See original GitHub issueThe use case concerns X500Principal (aka “distinguished name”) which can be mapped from/to byte array using a mixin class. Jackson serializes byte arrays as base64 strings by default. However, this failes when X500Principal is used as a Map key. In that case, the resulting byte array is serialized using toString(), which yields a useless output.
Complete example:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.x500.X500Principal;
public class Example
{
public static void main(String[] args) throws Exception
{
Map<X500Principal, X500Principal> values = new HashMap<>();
values.put(new X500Principal("CN=Mike"), new X500Principal("CN=John"));
new ObjectMapper()
.addMixIn(X500Principal.class, X500PrincipalMixin.class)
.writeValue(System.out, values);
}
private static final class X500PrincipalMixin
{
@JsonCreator
public X500PrincipalMixin(byte[] name) { throw new UnsupportedOperationException(); }
@JsonIgnore
public X500PrincipalMixin(String name) { throw new UnsupportedOperationException(); }
@JsonValue
public byte[] getEncoded() { throw new UnsupportedOperationException(); }
}
}
This outputs {"[B@1753acfe":"MA8xDTALBgNVBAMTBEpvaG4="}
. The value is correctly serialized as a base64 string, but the key isn’t. I tested this with 2.8.6, 2.8.7 and 2.9.0.pr1. I haven’t tested deserialization so far.
(Note that X500Principal cannot, in general, be serialized in its string representation (i.e., as per getName() and the String constructor), because the byte representation is the authoritative one and doesn’t round-trip through the string representation in the general case. Therefore a serialization based on the byte array is necessary.)
Issue Analytics
- State:
- Created 7 years ago
- Comments:5 (2 by maintainers)
Top GitHub Comments
This may actually be a combination of multiple problems. One problem would be that there may not (yet) be a key serializer for type
byte[]
; if so, I think that should be supported. There is related discussion on #1553 but leaving aside for the moment whether there should be lower level support, we should (I think) at least initially support Base64-encoded use. Similar support should obviously extend to deserialization side as well. For now I think this is the primary thing to verify – I can add test to verify “simple”Map<byte[],V>
case for reading/writing.Beyond this challenge there may actually be another problem: Java Type Erasure. In your case you are effectively serializing
Map<?,?>
(there’s a way to force type viaObjectWriter.forType(...)
/ObjectMapper.writerFor(...)
), and this may result in somewhat different serialization; and more importantly would be problematic for deserialization. This because nominal type ofjava.lang.Object
result in use of “natural” mapping, and distinction between JSON String meaning Java String vs JSON String meaningbyte[]
is lost.I mention this second problem for sake of completeness, as it is not really specific for this particular problem; but it may require minor other change. Note that another fix for this is to just use helper class like:
which binds key type (and if you want, value type too of course) and is NOT subject to Java Type Erasure (since type information is stored in class/sub-class relationship – there’s longer explanation but I digress).
Anyway: I will consider problem (1) now.
@kishor-p class of
byte[]
isbyte[].class
isn’t it?But as to not serializing as base64: what would you prefer? Array of numbers? I think there is a
@JsonFormat.shape
setting that actually allows that.