Java Jackson 保留对象类型信息序列化反序列化

问题描述

我正在尝试序列化和反序列化一个包含 Object 类型字段的类。当我为该字段分配一个变量时,例如一个整数,则该类的序列化版本不包含任何类型信息,无论是否使用 JsonTypeInfo 注释。我不知道如何继续,所以如果有人能指出我正确的方向

完整代码可以在我的 github 上找到: https://github.com/Notedop/BusinessRuleValidator

我已经检查了这个 SO:java jackson keep type information after serialize if variable of type Object 但是这个解决方案对我不起作用,因为 objectToEvaluate 字段在序列化后仍然不包含类型信息。

Field objectToEvaluate 是 Object 类型,但分配了一个 Integer 类型。这在序列化中不可见,并在反序列化时导致问题。理论上它应该能够分配任何类型,并且能够在不丢失原始类型信息的情况下正确地序列化和反序列化。

<BusinessRuleSet _class="nl.rvh.rulevalidation.BusinessRuleSet">
<name>Check Golden Cross</name>
<operator>AND</operator>
<successResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
    <parameters>
        <log>the result is SUCCESS!</log>
    </parameters>
</successResultApplicator>
<failResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
    <parameters>
        <log>the result is SUCCESS!</log>
    </parameters>
</failResultApplicator>
<businessRules>
    <businessRules _class="nl.rvh.rulevalidation.MaGoldenCross">
        <comparisonoperator>GREATER_THAN</comparisonoperator>
        <objectToEvaluate>2.0</objectToEvaluate>
        <name>MaGoldenCross</name>
        <successResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
            <parameters>
                <log>the result is SUCCESS!</log>
            </parameters>
        </successResultApplicator>
        <failResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
            <parameters>
                <log>the result is SUCCESS!</log>
            </parameters>
        </failResultApplicator>
    </businessRules>
    <businessRules _class="nl.rvh.rulevalidation.MaGoldenCross">
        <comparisonoperator>GREATER_THAN</comparisonoperator>
        <objectToEvaluate>10000000</objectToEvaluate>
        <name>MaGoldenCross</name>
        <successResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
            <parameters>
                <log>the result is SUCCESS!</log>
            </parameters>
        </successResultApplicator>
        <failResultApplicator _class="nl.rvh.rulevalidation.LogApplicator">
            <parameters>
                <log>the result is SUCCESS!</log>
            </parameters>
        </failResultApplicator>
    </businessRules>
</businessRules>

我猜序列化和反序列化的工作是可行的,因为值被分配给了 Field objectToEvaluate,但是类型信息丢失了,反序列化后它是一个 String 类型存储为 Object 而不是存储为 Object 的 Integer 类型.

一个名为 serializeAndDeserializeRuleSet() 的测试用例。此测试用例创建一个业务规则集,对其进行评估、序列化、反序列化,然后再次尝试对其进行评估。第二次运行时,由于反序列化问题导致类转换异常,它失败了。

出现问题的类是抽象 BusinessRule 类。该字段称为 objectToEvaluate

    package nl.rvh.rulevalidation;
    
    import com.fasterxml.jackson.annotation.*;
    import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
    import nl.rvh.rulevalidation.enums.Comparisonoperator;
    
    import javax.print.DocFlavor;
    
    /**
     * Abstract business rule class. Extend this class to implement custom business rule.
     * The implementing class must contain a constructor having the @JsonCreator annotation
     * and JsonProperty annotation on the constructor parameters to assure proper serialization
     * and de-serialization.
     *
     * Implement the Evaluate method in the derived class. You can process the objectToEvaluate to your
     * liking and then use the comparisonoperator.compare() method to do the final evaluation
     *
     */
    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,property = "@class",visible = true)
    public abstract class BusinessRule extends Rule {
    
        @JacksonXmlProperty
        protected Comparisonoperator comparisonoperator;
        @JacksonXmlProperty
        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,visible = true)
        protected Object objectToEvaluate;
    
        @JsonCreator
        protected BusinessRule(@JsonProperty("comparisonoperator") Comparisonoperator comparisonoperator,@JsonProperty("objectToEvaluate") Object objectToEvaluate,@JsonProperty("name") String name) {
            super(name);
            this.objectToEvaluate = objectToEvaluate;
            this.comparisonoperator = comparisonoperator;
        }
//getters and setters and stuff here
        }

派生类如下:

package nl.rvh.rulevalidation;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import nl.rvh.rulevalidation.enums.Comparisonoperator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,visible = true)
public class MaGoldenCross extends BusinessRule {

    private Logger log = LoggerFactory.getLogger(MaGoldenCross.class);

    @JsonCreator
    public MaGoldenCross(@JsonProperty("comparisonoperator") Comparisonoperator comparisonoperator,@JsonProperty("objectToEvaluate") Object expectedValue) {
        super(comparisonoperator,expectedValue,"MaGoldenCross");
    }

    @Override
    boolean evaluate(Object objectToEvaluate) {
        log.debug("Evaluating {} if {} is {} expected value {}",name,objectToEvaluate,comparisonoperator.getDescription(),this.objectToEvaluate);

        //do some stuff to the objectToEvaluate,or directly pass it for evaluation

        return comparisonoperator.compare(objectToEvaluate,getobjectToEvaluate());
    }

}

测试用例

@Test
void serializeAndDeserializeRuleSet() throws JsonProcessingException {
    XmlMapper xmlMapper = new XmlMapper();
    getBusinessRuleSet().evaluate(6);
    String xml = xmlMapper.writeValueAsstring(getBusinessRuleSet());
    log.debug(xml);
    BusinessRuleSet businessRuleSet = xmlMapper.readValue(xml,BusinessRuleSet.class);
    log.debug("{}",businessRuleSet);
    Assertions.assertTrue(businessRuleSet.evaluate(6)); // <---- FAILS as deserialized

}

private BusinessRuleSet getBusinessRuleSet() {
    BusinessRuleSet businessRules = new BusinessRuleSet("Check Golden Cross",LogicalOperator.AND);

    BusinessRule businessRule1 = new MaGoldenCross(GREATER_THAN,2.0);
    BusinessRule businessRule2 = new MaGoldenCross(GREATER_THAN,10000000);

    Map<String,Object> succesMap = new HashMap<>();
    succesMap.put("log","the result is SUCCESS!");

    LogApplicator successLogApplicator = new LogApplicator(succesMap);
    LogApplicator failLogApplicator = new LogApplicator(succesMap);

    businessRule1.setSuccessResultApplicator(successLogApplicator);
    businessRule2.setSuccessResultApplicator(successLogApplicator);
    businessRule1.setFailResultApplicator(failLogApplicator);
    businessRule2.setFailResultApplicator(failLogApplicator);

    businessRules.addRule(businessRule1);
    businessRules.addRule(businessRule2);

    businessRules.setSuccessResultApplicator(successLogApplicator);
    businessRules.setFailResultApplicator(failLogApplicator);
    return businessRules;
}

更新:

我一直在努力,但不幸的是,杰克逊仍然没有运气。作为最后的手段,我开始研究不同的序列化库。 xStream 能够完美地序列化/反序列化 java.lang.Object 并且仍然记住它的子类型信息。

public class SimplePojo {

    Object canBeAnySubType;

    public Object getCanBeAnySubType() {
        return canBeAnySubType;
    }

    public void setCanBeAnySubType(Object canBeAnySubType) {
        this.canBeAnySubType = canBeAnySubType;
    }
}



    @Test
    void testdamnPojoWithXstream() {


        SimplePojo pojo = new SimplePojo();
        pojo.setCanBeAnySubType(new Integer(1));

        XStream xstream = new XStream(new StaxDriver());
        String xml = xstream.toXML(pojo);
        log.debug(xml);

        SimplePojo deserPojo =  (SimplePojo) xstream.fromXML(xml);
        if (deserPojo.getCanBeAnySubType() instanceof Integer)
            log.debug("success");

    }

输出

2021-06-08 18:40:23:228 +0200 [main] DEBUG nl.rvh.rulevalidation.TestPojo - <?xml version='1.0' encoding='UTF-8'?><nl.rvh.rulevalidation.SimplePojo><canBeAnySubType class="int">1</canBeAnySubType></nl.rvh.rulevalidation.SimplePojo>
Security framework of XStream not explicitly initialized,using predefined black list on your own risk.
2021-06-08 18:40:23:251 +0200 [main] DEBUG nl.rvh.rulevalidation.TestPojo - success

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)