使用Jackson序列化和反序列化Lambda函数

3

我正在尝试对一个名为 RuleMessage 的类进行序列化和反序列化,但无法使其正常工作。这是我的代码:

public class RuleMessage {
    private String id;
    private SerializableRunnable sRunnable;

    public RuleMessage(String id, SerializableRunnable sRunnable) {
        this.id = id;
        this.sRunnable = sRunnable;
    }
}

public interface SerializableRunnable extends Runnable, Serializable {
}

    @Test
    public void testSerialization() throws JsonProcessingException {
        MAPPER.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, 
                                                               JsonTypeInfo.As.PROPERTY);
        
        SerializableRunnable r = () -> System.out.println("Serializable!");

        RuleMessage rule = new RuleMessage("1", r);
        System.out.println(MAPPER.writeValueAsString(businessRule));
}

我正在使用 Java 8。有人能告诉我这在 Jackson 库中是否可行吗?


首先,你不能将Runnable作为参数传递。你必须使用线程来实现。 - Suman
2个回答

3

Jackson的创建是为了保留对象的状态而不是行为。这就是为什么它试图使用getterssetters等序列化POJO的属性。序列化lambda表达式会破坏这个想法。因为没有任何属性可以序列化,只有应该被调用的方法。序列化原始的lambda对象是一个非常糟糕的想法,你应该重新设计你的应用程序来避免使用这样的情况。

在你的情况下,SerializableRunnable接口扩展了java.io.Serializable,这给出了一种选择-Java序列化。使用java.io.ObjectOutputStream,我们可以将lambda对象序列化为字节数组,并使用Base64编码在JSON负载中进行序列化。Jackson支持这种情况,提供writeBinarygetBinaryValue方法。

简单的例子可能如下所示:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class JsonLambdaApp {
    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        SerializableRunnable action = () -> System.out.println("Serializable!");

        String json = mapper.writeValueAsString(new RuleMessage("1", action));
        System.out.println(json);

        RuleMessage ruleMessage = mapper.readValue(json, RuleMessage.class);
        ruleMessage.getsRunnable().run();
    }
}

@JsonSerialize(using = LambdaJsonSerializer.class)
@JsonDeserialize(using = LambdaJsonDeserializer.class)
interface SerializableRunnable extends Runnable, Serializable {
}

class LambdaJsonSerializer extends JsonSerializer<SerializableRunnable> {

    @Override
    public void serialize(SerializableRunnable value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream)) {
            outputStream.writeObject(value);
            gen.writeBinary(byteArrayOutputStream.toByteArray());
        }
    }
}

class LambdaJsonDeserializer extends JsonDeserializer<SerializableRunnable> {
    @Override
    public SerializableRunnable deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        byte[] value = p.getBinaryValue();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(value);
             ObjectInputStream inputStream = new ObjectInputStream(byteArrayInputStream)) {
            return (SerializableRunnable) inputStream.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(e);
        }
    }
}

class RuleMessage {
    private String id;
    private SerializableRunnable sRunnable;

    @JsonCreator
    public RuleMessage(@JsonProperty("id") String id, @JsonProperty("sRunnable") SerializableRunnable sRunnable) {
        this.id = id;
        this.sRunnable = sRunnable;
    }

    public String getId() {
        return id;
    }

    public SerializableRunnable getsRunnable() {
        return sRunnable;
    }
}

以上代码输出 JSON
{
  "id" : "1",
  "sRunnable" : "rO0ABXNyACFqYXZhLmxhbmcuaW52b2tlLlNlcmlhbGl6ZWRMYW1iZGFvYdCULCk2hQIACkkADmltcGxNZXRob2RLaW5kWwAMY2FwdHVyZWRBcmdzdAATW0xqYXZhL2xhbmcvT2JqZWN0O0wADmNhcHR1cmluZ0NsYXNzdAARTGphdmEvbGFuZy9DbGFzcztMABhmdW5jdGlvbmFsSW50ZXJmYWNlQ2xhc3N0ABJMamF2YS9sYW5nL1N0cmluZztMAB1mdW5jdGlvbmFsSW50ZXJmYWNlTWV0aG9kTmFtZXEAfgADTAAiZnVuY3Rpb25hbEludGVyZmFjZU1ldGhvZFNpZ25hdHVyZXEAfgADTAAJaW1wbENsYXNzcQB+AANMAA5pbXBsTWV0aG9kTmFtZXEAfgADTAATaW1wbE1ldGhvZFNpZ25hdHVyZXEAfgADTAAWaW5zdGFudGlhdGVkTWV0aG9kVHlwZXEAfgADeHAAAAAGdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAAHZyABxjb20uY2Vsb3hpdHkuSnNvblR5cGVJbmZvQXBwAAAAAAAAAAAAAAB4cHQAIWNvbS9jZWxveGl0eS9TZXJpYWxpemFibGVSdW5uYWJsZXQAA3J1bnQAAygpVnQAHGNvbS9jZWxveGl0eS9Kc29uVHlwZUluZm9BcHB0ABZsYW1iZGEkbWFpbiQ1YzRiNmEwOCQxcQB+AAtxAH4ACw=="
}

以及lambda函数:

Serializable!

另请参阅:


0
首先,在 RuleMessage 中,您必须创建 getter / setter 或将字段设置为 public,以便提供 Jackson 对字段的访问权限。
然后,您的代码会打印出类似以下内容的信息:
{"@class":"RuleMessage","id":"1","sRunnable":{"@class":"RuleMessage$$Lambda$20/0x0000000800b91c40"}}

无法反序列化此JSON文档,因为RuleMessage没有默认构造函数,也无法构造lambda表达式。

您可以创建一个类来替代lambda表达式:

  public class Runner implements SerializableRunnable {
    @Override
    public void run() {
      System.out.println("Serializable!");
    }
  }

并且像这样构建您的POJO:

new RuleMessage("1", new Runner())

Jackson反序列化器现在能够重构对象并执行运行程序。


感谢您的回复。我确实有一个默认构造函数和getter/setter,但为了简洁起见将它们删除了(应该提到这一点)。我想要一个灵活的设计,可以将方法调用包装在lambda函数中并将其序列化到数据库中。稍后,我想反序列化lambda并调用它。我尝试在函数内部实现类,但Jackson无法序列化它。如果将类移到函数外部,则可以正常工作。是否有一种方法可以在Jackson中序列化内部类或lambda? - Prince

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接