Как вы сериализуете неизменные коллекции Гуавы, используя Protostuff?

Я использую protostuff-runtime для сериализации графов объектов. Некоторые из этих объектов имеют ссылки на неизменяемые коллекции Guava, такие как ImmutableList и ImmutableSet. Protostuff не может десериализовать эти коллекции из коробки, потому что он пытается создать экземпляр и затем "добавить" к нему элементы из inputStream (что не удается, так как коллекции являются неизменяемыми).

Знаете ли вы какой-либо плагин библиотеки / protostuff, который делает это из коробки? Если нет, то есть ли лучший способ сделать это сам?

Я исследовал и обнаружил, что у protostuff есть понятие "делегат", которое позволяет вам контролировать сериализацию для определенных типов. Кажется, это ответ на мою проблему, но я не могу заставить ее работать.

Вот что у меня сейчас есть:

import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;

/**
 * This is the POJO I want to serialize. Note that the {@code strings} field refers to an {@link ImmutableList}.
 */
@Immutable
public class Foo {

    public static final Schema<Foo> SCHEMA = RuntimeSchema.getSchema(Foo.class);

    @Nonnull
    private final ImmutableList<String> strings;

    public Foo(ImmutableList<String> strings) {
        this.strings = Preconditions.checkNotNull(strings);
    }

    @Nonnull
    public ImmutableList<String> getStrings() {
        return strings;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Foo) {
            Foo that = (Foo) obj;
            return this.strings.equals(that.strings);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return strings.hashCode();
    }

}

import com.dyuproject.protostuff.*;
import com.dyuproject.protostuff.runtime.Delegate;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import java.io.IOException;
import java.util.ArrayList;

public class ImmutableListDelegate implements Delegate<ImmutableList<?>> {

    private static final Schema<ArrayList> LIST_SCHEMA = RuntimeSchema.getSchema(ArrayList.class);

    @Override
    public WireFormat.FieldType getFieldType() {
        return WireFormat.FieldType.MESSAGE;
    }

    @Override
    public ImmutableList<?> readFrom(Input input) throws IOException {
        ArrayList<?> list = LIST_SCHEMA.newMessage();
        input.mergeObject(list, LIST_SCHEMA);
        return ImmutableList.copyOf(list);
    }

    @Override
    public void writeTo(Output output, int number, ImmutableList<?> value, boolean repeated) throws IOException {
        ArrayList<?> list = Lists.newArrayList(value);
        output.writeObject(number, list, LIST_SCHEMA, repeated);
        LIST_SCHEMA.writeTo(output, list);
    }

    @Override
    public void transfer(Pipe pipe, Input input, Output output, int number, boolean repeated) throws IOException {
        throw new UnsupportedOperationException("TODO");
    }

    @Override
    public Class<?> typeClass() {
        return ImmutableList.class;
    }
}

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.runtime.DefaultIdStrategy;
import com.dyuproject.protostuff.runtime.RuntimeEnv;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ImmutableListDelegateTest {

    @Before
    public void before() {
        // registers the delegate
        if (RuntimeEnv.ID_STRATEGY instanceof DefaultIdStrategy) {
            ((DefaultIdStrategy) RuntimeEnv.ID_STRATEGY).registerDelegate(new ImmutableListDelegate());
        }
    }

    @Test
    public void testDelegate() throws IOException {
        Foo foo = new Foo(ImmutableList.of("foo"));

        Assert.assertEquals(foo, serializeThenDeserialize(foo));
    }

    private Foo serializeThenDeserialize(Foo fooToSerialize) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ProtostuffIOUtil.writeDelimitedTo(out, fooToSerialize, Foo.SCHEMA, buffer());
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        Foo fooDeserialized = Foo.SCHEMA.newMessage();
        ProtostuffIOUtil.mergeDelimitedFrom(in, fooDeserialized, Foo.SCHEMA, buffer());
        return fooDeserialized;
    }

    private LinkedBuffer buffer() {
        return LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    }
}

Сбой теста со следующим исключением, которое, кажется, означает, что мой делегат десериализует только нулевые значения:

java.lang.NullPointerException
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191)
    at com.google.common.collect.SingletonImmutableList.<init>(SingletonImmutableList.java:40)
    at com.google.common.collect.ImmutableList.asImmutableList(ImmutableList.java:305)
    at com.google.common.collect.ImmutableList.copyFromCollection(ImmutableList.java:314)
    at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:253)
    at test.ImmutableListDelegate.readFrom(ImmutableListDelegate.java:25)
    at test.ImmutableListDelegate.readFrom(ImmutableListDelegate.java:12)
    at com.dyuproject.protostuff.runtime.RuntimeUnsafeFieldFactory$19$1.mergeFrom(RuntimeUnsafeFieldFactory.java:1111)
    at com.dyuproject.protostuff.runtime.MappedSchema.mergeFrom(MappedSchema.java:188)
    at com.dyuproject.protostuff.IOUtil.mergeDelimitedFrom(IOUtil.java:109)
    at com.dyuproject.protostuff.ProtostuffIOUtil.mergeDelimitedFrom(ProtostuffIOUtil.java:151)
    at test.ImmutableListDelegateTest.serializeThenDeserialize(ImmutableListDelegateTest.java:38)
    at test.ImmutableListDelegateTest.testDelegate(ImmutableListDelegateTest.java:30)

Это правильный подход? Что мне не хватает?

Это не дубликат исключения " Что такое пустой указатель", и как мне это исправить? вопрос, который не имеет смысла. Тот факт, что я упомянул, что NPE выбрасывается при попытке использовать делегат Protostuff для десериализации неизменяемых коллекций, не означает, что это дублирует "Что такое NPE?" вопрос в любой форме, форме или форме.

1 ответ

Все выглядит хорошо и

java.lang.NullPointerException
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191)
    at com.google.common.collect.SingletonImmutableList.<init>(SingletonImmutableList.java:40)

говорит, что вы пытаетесь поставить null в ImmutableList, что запрещено. Чтобы быть уверенным, осмотреть вас list как раз перед неудачной линией. Убедитесь, что ваш ввод JSON не похож [null],

Другие вопросы по тегам