KevinS
KevinS

Reputation: 7875

How to store a Map in a Hazelcast Map?

I have a class that has a field of type Map. I'm creating a CompactSerializer to read and write this class to a map in Hazelcast (version 5.1). But I can't figure out how to serialize the map field. There are no methods on the CompactReader or CompactWriter that work with Maps.

Here's a simplified version of the class and it's incomplete serializer:

data class User(
    val id: String,
    val authoritiesByOrgId: Map<String, Set<String>>
)
class UserSerializer: CompactSerializer<User> {


    override fun read(reader: CompactReader): User {

        val id = reader.readString("id")
        val authoritiesByOrgId = // What goes here?

        return User(
                id,
                authoritiesByOrgId
        )

    }


    override fun write(writer: CompactWriter, user: User) {

        writer.apply {

            writeString("id", user.id)
            write???("authoritiesByOrgId", user.authoritiesByOrgId) // TODO What goes here?

        }

    }


}

Upvotes: 1

Views: 492

Answers (1)

mdumandag
mdumandag

Reputation: 266

This is a bit hard because of two things. Right now, Compact related APIs do not support

  • Maps
  • Collections inside collections

However, there are workarounds. One could write a Compact serializer for your class as below:

class UserSerializer implements CompactSerializer<User> {

    @Override
    public User read(CompactReader reader) {
        String id = reader.readString("id");
        String[] keys = reader.readArrayOfString("orgIds");
        SetWrapper[] values = reader.readArrayOfCompact("authorities", SetWrapper.class);
        HashMap<String, Set<String>> authoritiesByOrgId = new HashMap<>(keys.length);
        for (int i = 0; i < keys.length; i++) {
            authoritiesByOrgId.put(keys[i], values[i].getSet());
        }
        return new User(id, authoritiesByOrgId);
    }

    @Override
    public void write(CompactWriter writer, User object) {
        // Write id as it is
        writer.writeString("id", object.getId());

        // Serialize map as 2 arrays, one for keys, one for values
        Map<String, Set<String>> authoritiesByOrgId = object.getAuthoritiesByOrgId();
        
        // keys
        writer.writeArrayOfString("orgIds",
                authoritiesByOrgId.keySet().toArray(new String[0]));

        // Since arrays of arrays are not supported yet, wrap the array in
        // another type
        writer.writeArrayOfCompact("authorities",
                authoritiesByOrgId.values().stream()
                        .map(SetWrapper::new)
                        .toArray());
    }

    @Override
    public String getTypeName() {
        return "user";
    }

    @Override
    public Class<User> getCompactClass() {
        return User.class;
    }
}

class SetWrapperSerializer implements CompactSerializer<SetWrapper> {

    @Override
    public SetWrapper read(CompactReader reader) {
        String[] set = reader.readArrayOfString("set");
        return new SetWrapper(new HashSet<>(Arrays.asList(set)));
    }

    @Override
    public void write(CompactWriter writer, SetWrapper object) {
        object.getSet().toArray(new String[0]);
        writer.writeArrayOfString("set", object.getSet().toArray(new String[0]));
    }

    @Override
    public String getTypeName() {
        return "set-wrapper";
    }

    @Override
    public Class<SetWrapper> getCompactClass() {
        return SetWrapper.class;
    }
}

class SetWrapper {
    private final Set<String> set;

    SetWrapper(Set<String> set) {
        this.set = set;
    }

    public Set<String> getSet() {
        return set;
    }
}

class User {
    private final String id;
    private final Map<String, Set<String>> authoritiesByOrgId;


    User(String id, Map<String, Set<String>> authoritiesByOrgId) {
        this.id = id;
        this.authoritiesByOrgId = authoritiesByOrgId;
    }

    public String getId() {
        return id;
    }

    public Map<String, Set<String>> getAuthoritiesByOrgId() {
        return authoritiesByOrgId;
    }
}

So, the core ideas are

  • serialize a map as 2 arrays: keys and values
  • use a wrapper type to be able to serialize "arrays of arrays"

Upvotes: 2

Related Questions