question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Map key converted to byte array is not serialized as base64 string

See original GitHub issue

The 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:closed
  • Created 7 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
cowtowncodercommented, Mar 11, 2017

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 via ObjectWriter.forType(...) / ObjectMapper.writerFor(...)), and this may result in somewhat different serialization; and more importantly would be problematic for deserialization. This because nominal type of java.lang.Object result in use of “natural” mapping, and distinction between JSON String meaning Java String vs JSON String meaning byte[] 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:

class BinaryKeyMap<V> extends HashMap<byte[], T> { }

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.

0reactions
cowtowncodercommented, May 9, 2019

@kishor-p class of byte[] is byte[].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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Problems converting byte array to string and back to byte array
You first need to convert the bytes to something that can be presented as a string. Usually by converting to hex or base64....
Read more >
Convert String to Byte Array and Reverse in Java - Baeldung
This class specifies a mapping between a sequence of chars and a sequence of bytes. We refer to the above process as encoding....
Read more >
RFC 7049: Concise Binary Object Representation (CBOR)
The array's length follows the rules for byte strings (major type 2), ... A map that has duplicate keys may be well-formed, but...
Read more >
Serialization Formats - ksqlDB Documentation
Bytes are serialized as a Base64-encoded string value. For example, the byte array [61, 62, 63] is serialized as: ...
Read more >
Prefixes, Derivation and derivation reference tables | keri
Although a Base64 expression of a 32 byte binary array of bytes is not very ... When converting such a string of bytes...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found