Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
nmatt opened this issue Mar 10, 2017 · 5 comments
Closed

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

nmatt opened this issue Mar 10, 2017 · 5 comments
Milestone

Comments

@nmatt
Copy link

nmatt commented Mar 10, 2017

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.)

@cowtowncoder
Copy link
Member

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.

@cowtowncoder cowtowncoder added this to the 2.9.0.pr2 milestone Mar 13, 2017
@kishor-p
Copy link

kishor-p commented May 9, 2019

Jackson serializes byte arrays as base64 strings by default

Can someone please tell me how to stop jackson from serializing byte arrays to base64 string

@plokhotnyuk
Copy link

plokhotnyuk commented May 9, 2019

@kishor-p you can use a custom serializer like here.

@kishor-p
Copy link

kishor-p commented May 9, 2019

@plokhotnyuk Any java implementation? I have been trying but having some troubles with native byte[] not having a class.

@cowtowncoder
Copy link
Member

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants