【问题标题】:How do you serialize Guava's immutable collections using Protostuff?你如何使用 Protostuff 序列化 Guava 的不可变集合?
【发布时间】:2012-09-10 13:17:33
【问题描述】:

我使用 protostuff-runtime 来序列化对象图。其中一些对象引用了 Guava 不可变集合,例如 ImmutableList 和 ImmutableSet。 Protostuff 无法开箱即用地反序列化这些集合,因为它会尝试构造一个实例,然后从 inputStream 中“添加”元素(这会失败,因为集合是不可变的)。

你知道任何开箱即用的库/原型插件吗?如果没有,是否有自己做的最佳实践?

我进行了调查,发现 protostuff 有一个 "delegate" 的概念,可以让您控制特定类型的序列化。这似乎是我的问题的答案,但我似乎无法让它工作。

这是我现在拥有的:

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)

这是正确的方法吗?我错过了什么?

这不是What is a Null Pointer Exception, and how do I fix it? 问题的重复,这没有任何意义。我提到在尝试使用 Protostuff 委托对不可变集合进行反序列化时抛出 NPE 的事实并不意味着这重复了“什么是 NPE?”。以任何方式、形式或形式提出问题。

【问题讨论】:

  • 嗨,对protostuff不太了解,但我确实发现过一次这个错误。它是在 Guava 实现中......我更改为一个副本列表(这对我来说很好)......
  • @PlínioPantaleão 我现在的解决方法是存储一个 ArrayList(在构造函数中构造一个新的 ArrayList,并在 getter 中构造一个 ImmutableList)。但我想避免这样做:/
  • 我在 guava 版本 11 上发现了这个错误。你应该看看它在当前版本中是否仍然发生
  • 我正在使用 Guava 13.0.1,但我认为这不是 Guava 错误。更多的是 protostuff 期望集合在反序列化/合并期间是可变的,而 Guava 的不可变集合并非如此。
  • 我不这么认为。如果这是真的,那么您的 Delegate 方法应该已经解决了问题吧?

标签: java serialization protocol-buffers guava protostuff


【解决方案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]

【讨论】:

  • 问题确实是我试图用null 元素构造一个ImmutableList。但是我不明白为什么当我在 Delegate 的 readFrom() 方法中执行 ArrayList&lt;?&gt; list = LIST_SCHEMA.newMessage(); input.mergeObject(list, LIST_SCHEMA); 时,protostuff 会返回一个包含 null 元素的列表。我的 writeTo()readFrom() 方法可能是错误的,或者我可能错误地使用了 RuntimeSchema。我只是想让一个代表告诉 protostuff “好吧,与其序列化这个 ImmutableList,不如用这个 ArrayList 来序列化它,因为你知道怎么做”。
  • 我希望你能自己解决这部分......我做到了with Gson,如果没有泛型,这将是微不足道的。您可以在不知道其成员类型的情况下反序列化任意 ArrayList 吗?
  • 这实际上是我坚持我的方法的地方。我真的不明白如何要求 protostuff 序列化通用列表。我本可以将我的问题措辞专门询问那一点信息,但我想知道是否有比我的代表更好的方法。
  • @eneveu:不知道......也许在这里开始另一个问题或寻找一个邮件列表。这涉及ImmutableList&lt;T&gt; 问题,假设List&lt;T&gt; 问题已经解决。
猜你喜欢
  • 2013-05-05
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多