Jackson ObjectMapper-指定对象属性的序列化顺序


80

我正在实现RESTful Web服务,其中用户必须与请求一起发送签名的验证令牌,以便可以确保请求不会被中间人篡改。我当前的实现如下。

验证令牌是VerifData对象,序列化为String,然后进行哈希处理和加密。

class VerifData {
    int prop1;
    int prop2;
}

在我的服务中,我将要序列化的数据放入VerifData的实例中,然后使用Jackson ObjectMapper对其进行序列化,并与验证令牌一起传递给验证引擎。

VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);

但是似乎每次启动应用程序容器时,ObjectMapper映射到字符串的属性的顺序都会改变。

例如:一次

{"prop1":12345,"prop2":67890}

再过一次

{"prop2":67890,"prop1":12345}

因此,如果客户端将VerifData实例序列化为第一个String,则即使它是正确的,也有50%的机会失败。

有办法解决这个问题吗?是否可以指定要按ObjectMapper映射的属性的顺序(如升序)?还是有其他方法可以最好地实施此验证步骤。客户端和服务器实现都是我开发的。我使用Java Security API进行签名和验证。

Answers:


80

Jackson注释文档中

// ensure that "id" and "name" are output before other properties
@JsonPropertyOrder({ "id", "name" })

// order any properties that don't have explicit setting using alphabetic order
@JsonPropertyOrder(alphabetic=true)

从链接“所有codehaus服务都已终止”
安德鲁·诺曼

2
如下所述,如果您的目标是在所有位置应用排序,而不是在序列化特定对象时,请参阅stackoverflow.com/a/46267506/2089674
user2089674

89

注释很有用,但可能难以应用到所有地方。您可以配置整个ObjectMapper以这种方式与

当前的Jackson版本: objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

较旧的Jackson版本: objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);


这不是很正确(或者至少在所有情况下都行不通)-有关工作原理的完整示例,请参见stackoverflow.com/a/46267506/2089674
user2089674

2
@ user2089674-您的示例仅适用于Map键。OP要求针对对象字段的解决方案。
戴夫

9

在Spring Boot中,您可以通过将以下内容添加到Application入口点类来全局添加此行为:

  @Bean
  public Jackson2ObjectMapperBuilder objectMapperBuilder() {

    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

    return builder;
  }

9

通过指定一个属性,Spring Boot中有一个更简单的方法(application.properties例如:

spring.jackson.mapper.sort_properties_alphabetically=true

8

在您今天可能正在使用的Jackson 2.x中,使用:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

如果您关心外观,那么也可以考虑SerializationFeature.INDENT_OUTPUT

请注意,您必须序列化MapObjects才能正确排序。如果您序列化了一个JsonNode示例(来自readTree),则不会正确缩进。

import com.fasterxml.jackson.databind.*;

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));

结果是:

{
  "hello" : {
    "cruel" : "world"
  }
}

6
只有一个地图条目。因此,我看不到它的顺序。

7

需要以下2个ObjectMapper配置:

ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
要么
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)

定义用于POJO属性系列化顺序字段
:确实适用java.util.Map系列化!


ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
要么
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)

决定是否 java.util.Map序列化之前首先按键条目进行排序的功能


Spring Boot配置示例(yaml):

spring:
  jackson:
    mapper:
      SORT_PROPERTIES_ALPHABETICALLY: true
    serialization:
      ORDER_MAP_ENTRIES_BY_KEYS: true

3

来自Duncan McGregor的回答:最好像这样使用它:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

因为MapperFeature是用于XML的,并且附带了jackson-databind,所以不需要...


0

而不使用标志参数:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

0

您可以使用混入并根据需要指定属性的顺序:

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public final class ObjectMapperUtils {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.addMixIn(Object.class, IdFirst.class);
    }

    @Bean
    public ObjectMapper objectMapper() {
        return MAPPER;
    }

    @JsonPropertyOrder({"id", "...", "..."})
    private abstract static class IdFirst {}

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.