在Spring中编写JSON反序列化程序的正确方法或扩展其功能

66

我正在尝试在Spring中编写自定义的JSON反序列化器。我希望大部分字段使用默认的反序列化器,仅对少数属性使用自定义的反序列化器。这种方式可行吗? 我之所以采用这种方式是因为,大部分属性都是值类型,所以我可以让Jackson使用默认的反序列化器;但是有几个属性是引用类型,所以在自定义的反序列化器中,我必须查询数据库以获取引用名称并从数据库中获取引用值。

如果需要,我可以展示一些代码。


请澄清一下,如何确定是否使用自定义处理:通过字段名称(有效地),还是通过字段类型? - Programmer Bruce
抱歉,我没有看到这个@ProgrammerBruce的评论.. 我认为是指字段类型.. - gc5
4个回答

102

我已经搜索了很多,目前发现最好的方法在这篇文章中:

用于序列化的类

package net.sghill.example;

import net.sghill.example.UserDeserializer
import net.sghill.example.UserSerializer
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;

@JsonDeserialize(using = UserDeserializer.class)
public class User {
    private ObjectId id;
    private String   username;
    private String   password;

    public User(ObjectId id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public ObjectId getId()       { return id; }
    public String   getUsername() { return username; }
    public String   getPassword() { return password; }
}

反序列化类

package net.sghill.example;

import net.sghill.example.User;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.ObjectCodec;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;

import java.io.IOException;

public class UserDeserializer extends JsonDeserializer<User> {

    @Override
    public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        ObjectCodec oc = jsonParser.getCodec();
        JsonNode node = oc.readTree(jsonParser);
        return new User(null, node.get("username").getTextValue(), node.get("password").getTextValue());
    }
}

编辑:

或者您可以查看这篇文章,该文章使用com.fasterxml.jackson.databind.JsonDeserializer的新版本。


87
嘿,那是我的文章!很高兴你发现它有用 :) - sghill
2
在新版本的 Jackson 中,在 com.fasterxml.jackson.databind.JsonDeserializer 类中使用 asText() 而不是 getTextValue() 来获取文本。谢谢!+1 :) - albciff
但如果它不是我的自定义类。 - gstackoverflow
1
我在使用这种方法时遇到了“oc.readTree”的NPE错误。有人知道如何处理吗? - Normal
4
Jackson 现在建议扩展StdDeserializer<Foo>类,供您参考。 - coderatchet
1
这对我有用。我的使用情况是仅针对一个字段使用自定义逻辑。有没有办法做到这一点?所讨论的方法需要为所有成员变量编写逻辑。 - narendra-choudhary

11
我将尝试将一个由Spring管理的服务注入到我的Deserializer中。有人向我提示,Jackson在调用序列化器/反序列化器时使用new操作符。这意味着无法自动装配Jackson实例中的Deserializer。下面是我如何将我的服务类@AutowireDeserializer中的方法:

context.xml

<mvc:annotation-driven>
  <mvc:message-converters>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <property name="objectMapper" ref="objectMapper" />
    </bean>
  </mvc:message-converters>
</mvc>
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
    <!-- Add deserializers that require autowiring -->
    <property name="deserializersByType">
        <map key-type="java.lang.Class">
            <entry key="com.acme.Anchor">
                <bean class="com.acme.AnchorDeserializer" />
            </entry>
        </map>
    </property>
</bean>

现在我的Deserializer是一个由Spring管理的bean,自动装配可以正常工作!

AnchorDeserializer.java

public class AnchorDeserializer extends JsonDeserializer<Anchor> {
    @Autowired
    private AnchorService anchorService;
    public Anchor deserialize(JsonParser parser, DeserializationContext context)
             throws IOException, JsonProcessingException {
        // Do stuff
    }
}

AnchorService.java

@Service
public class AnchorService {}

更新:尽管我最初的回答在我撰写这篇文章时有效,但 @xi.lin 的回答才是所需的。很好的发现!

1
您的解决方案对我没有帮助。我使用 SpringBeanAutowiringSupport 解决了这个问题。 - Peter Jurkovic
4
我认为你也可以看到这个链接,它使用HandlerInstantiator来完成这个任务。 - xi.lin
有趣的是,这种方法对某些人有效,而对另一些人则无效。@PeterJurkovič使用的这种方法对我没用,但这种方法有效。 - Vivin Paliath
也许在后续版本中已经修复了。我不记得我写这个时使用的是哪个Spring版本,但那应该是非常重要的注明。 - Beez
如果您想在没有XML配置的情况下使用它,请查看我的答案 - LeO

1

使用Spring MVC 4.2.1.RELEASE版本时,您需要按照以下方式使用新的Jackson2依赖项才能使反序列化程序正常工作。

不要使用此方法

<dependency>  
            <groupId>org.codehaus.jackson</groupId>  
            <artifactId>jackson-mapper-asl</artifactId>  
            <version>1.9.12</version>  
        </dependency>  

请使用这个替代方案。
<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.2.2</version>
        </dependency>  

还要使用com.fasterxml.jackson.databind.JsonDeserializercom.fasterxml.jackson.databind.annotation.JsonDeserialize进行反序列化,而不是来自org.codehaus.jackson的类。


0
  1. 如果您想要覆盖特定属性的默认反序列化器,您可以标记这些属性和您想要使用的反序列化器,例如:

--

// root json object
public class Example {
  private String name;
  private int value;
  // You will implement your own deserilizer/Serializer for the field below//
  @JsonSerialize(converter = AddressSerializer.class)
  @JsonDeserialize(converter = AddressDeserializer.class)
  private String address;
}

这里有一个完整的例子。

  1. 如果您想在Spring应用程序上下文中使用非Spring管理的对象映射器,并配置序列化程序/反序列化程序以使用Spring管理的服务来查询数据库,可以通过告诉Jackson使用 Spring Handler instantiator 来创建Deserialisers / Serialisers实例来实现。

在您的应用程序上下文配置中,创建带有 SpringHandlerInstantiator ObjectMapper bean,例如:

@Autowired
ApplicationContext applicationContext;

@Bean    
public ObjectMapper objectMapper(){
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.handlerInstantiator(handlerInstantiator());
    // add additional configs, etc.. here if needed
    return builder.build();
}

@Bean
public HandlerInstantiator handlerInstantiator(){
    return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}

然后你可以在 objectMapper 上方使用 @Autowire 来反序列化 json:

@Autowired
ObjectMapper objectMapper;

public Something readSomething(...){
    ...
    Something st = objectMapper.readValue(json, Something.class);
    ...
    return st;
}

无论您想使用哪种反序列化器,例如:字段或类,您都可以在反序列化器中使用spring上下文,这意味着您可以将您的服务或由同一ApplicationContext管理的任何spring bean自动装配到其中。
public class MyCustomDeserialiser extends ..{
      
    @Autowired;
    MyService service;

    @AutoWired
    SomeProperties properties;

    @Override
    public MyValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    ....
    }
    ...
}

此外,您可以在这里找到Jackson反序列化器的示例。


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