【问题标题】:Jackson renames primitive boolean field by removing 'is'杰克逊通过删除“是”重命名原始布尔字段
【发布时间】:2015-11-23 01:52:40
【问题描述】:

这可能是重复的。但我找不到我的问题的解决方案。

我有课

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Getter 和 setter 由 Eclipse 生成。

在另一个类中,我将值设置为 true,并将其写为 JSON 字符串。

System.out.println(new ObjectMapper().writeValueAsString(myResponse));

在 JSON 中,密钥为 {"success": true}

我希望密钥为isSuccess 本身。 Jackson 在序列化时是否使用了 setter 方法?如何使键成为字段名称本身?

【问题讨论】:

  • 如果你的属性名是 liek isSuccessyou 方法名必须是 isIsSuccess 我认为
  • 我明白了。我认为SetSuccess 更好,因为它是由 Eclipse 生成的。 (遵循标准)

标签: java json jackson


【解决方案1】:

这是一个稍晚的答案,但可能对访问此页面的其他人有用。

在序列化为 JSON 时更改 Jackson 将使用的名称的简单解决方案是使用 @JsonProperty 注释,因此您的示例将变为:

public class MyResponse implements Serializable {

    private boolean isSuccess;

    @JsonProperty(value="isSuccess")        
    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

这将被序列化为 JSON 为 {"isSuccess":true},但优点是不必修改您的 getter 方法名称。

请注意,在这种情况下,您也可以将注释写为 @JsonProperty("isSuccess"),因为它只有一个 value 元素

【讨论】:

  • 此方法不适用于我的情况,因为该类不属于我,因为它来自第 3 方依赖项。对于这种情况,请参阅下面的答案。
  • 我使用 spring boot 和 jackson,但得到两个字段一个是“success”,另一个是“isSuccess”,当我使用非原始布尔值时,比只有一个字段“isSuccess”
  • @VishalSingla 我也有同样的问题,这个解决方案在 Spring Boot 中产生了两个字段
  • @VishalSingla JsonProperty 在 getter 上的注释。
  • 这似乎是正确答案stackoverflow.com/a/55567974/8283737
【解决方案2】:

我最近遇到了这个问题,这就是我发现的。 Jackson 将检查您传递给它的任何类的 getter 和 setter,并使用这些方法进行序列化和反序列化。这些方法中“get”、“is”和“set”后面的内容将用作 JSON 字段的键(getIsValid 和 setIsValid 为“isValid”)。

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }
} 

同样,“isSuccess”会变成“success”,除非重命名为“isIsSuccess”或“getIsSuccess”

在此处阅读更多信息:http://www.citrine.io/blog/2015/5/20/jackson-json-processor

【讨论】:

  • isValid 不是 java 中布尔数据类型的正确命名约定。应该是有效的并且isValid(), setValid()
  • 但不应该是这样吗?一个公约?如果确实存在,您能否链接到 Jackson 参考,说明它使用 getter 名称作为 JSON 字段?还是您认为这是一个糟糕的设计选择?
  • 我希望对此有警告
  • @vels4j 当您处理高度具体的实现时,命名约定就会消失。
【解决方案3】:

使用下面的两个注释,强制输出 JSON 包含is_xxx

@get:JsonProperty("is_something")
@param:JsonProperty("is_something")

【讨论】:

  • 这是这个问题的最佳答案。
  • 那是 Java 吗?也许是 Kotlin?
【解决方案4】:

当您使用 Kotlin 和数据类时:

data class Dto(
    @get:JsonProperty("isSuccess") val isSuccess: Boolean
)

如果您还要反序列化 JSON,则可能需要添加 @param:JsonProperty("isSuccess")

【讨论】:

    【解决方案5】:

    您可以按如下方式配置您的ObjectMapper

    mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
                @Override
                public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
                {
                    if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
                            && method.getName().startsWith("is")) {
                        return method.getName();
                    }
                    return super.nameForGetterMethod(config, method, defaultName);
                }
            });
    

    【讨论】:

    • 我喜欢你试图通过配置解决这个问题。但是,这仅在您总是 为布尔字段和 JSON 属性加上“is”前缀时才有效。假设您有另一个简单命名为“已启用”的布尔字段,您希望将其序列化。因为生成的方法是“isEnabled()”,所以上面的代码会将其序列化为“isEnabled”,而不仅仅是“enabled”。最终,问题是对于“x”和“isX”这两个字段,Eclipse 都会生成方法“isX()”;因此您无法推断出与该字段匹配的属性名称。
    • @DavidSiegal 基于 burak 的回答我已经扩展了下面的答案以支持这种情况。
    【解决方案6】:

    基于 Utkarsh 的回答..

    Getter 名称减去 get/is 用作 JSON 名称。

    public class Example{
        private String radcliffe; 
    
        public getHarryPotter(){
            return radcliffe; 
        }
    }
    

    存储为 { "harryPotter" : "whateverYouGaveHere" }


    对于反序列化,Jackson 检查设置器和字段名称。 对于 Json 字符串 { "word1" : "example" },以下两个都有效。

    public class Example{
        private String word1; 
    
        public setword2( String pqr){
            this.word1 = pqr; 
        }
    }
    
    public class Example2{
        private String word2; 
    
        public setWord1(String pqr){
            this.word2 = pqr ; 
        }
    }
    

    一个更有趣的问题是 Jackson 考虑反序列化的顺序。如果我尝试用

    反序列化 { "word1" : "myName" }
    public class Example3{
        private String word1;
        private String word2; 
    
        public setWord1( String parameter){
            this.word2 = parameter ; 
        }
    }
    

    我没有测试上面的情况,但是看看 word1 & word2 的值会很有趣 ...

    注意:我使用了截然不同的名称来强调哪些字段必须相同。

    【讨论】:

      【解决方案7】:

      我不想弄乱一些自定义命名策略,也不想重新创建一些访问器。
      代码越少,我越快乐。

      这对我们有用:

      import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      @JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates 
      public class MyResponse {
      
          private String id;
          private @JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
          private @JsonProperty("isDeleted") boolean isDeleted;
      
      }
      

      【讨论】:

        【解决方案8】:

        这个问题还有另一种方法。

        只需定义一个新的子类 extends PropertyNamingStrategy 并将其传递给 ObjectMapper 实例。

        这里有一个代码 sn-p 可能会有更多帮助:

        mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
                @Override
                public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
                    String input = defaultName;
                    if(method.getName().startsWith("is")){
                        input = method.getName();
                    }
        
                    //copy from LowerCaseWithUnderscoresStrategy
                    if (input == null) return input; // garbage in, garbage out
                    int length = input.length();
                    StringBuilder result = new StringBuilder(length * 2);
                    int resultLength = 0;
                    boolean wasPrevTranslated = false;
                    for (int i = 0; i < length; i++)
                    {
                        char c = input.charAt(i);
                        if (i > 0 || c != '_') // skip first starting underscore
                        {
                            if (Character.isUpperCase(c))
                            {
                                if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                                {
                                    result.append('_');
                                    resultLength++;
                                }
                                c = Character.toLowerCase(c);
                                wasPrevTranslated = true;
                            }
                            else
                            {
                                wasPrevTranslated = false;
                            }
                            result.append(c);
                            resultLength++;
                        }
                    }
                    return resultLength > 0 ? result.toString() : input;
                }
            });
        

        【讨论】:

          【解决方案9】:

          接受的答案不适用于我的情况。

          就我而言,该课程不归我所有。有问题的类来自第 3 方依赖,所以我不能只在其中添加 @JsonProperty 注释。

          为了解决这个问题,受上述@burak 回答的启发,我创建了一个自定义PropertyNamingStrategy,如下所示:

          mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            @Override
            public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
            {
              if (method.getParameterCount() == 1 &&
                      (method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
                      method.getName().startsWith("set")) {
          
                Class<?> containingClass = method.getDeclaringClass();
                String potentialFieldName = "is" + method.getName().substring(3);
          
                try {
                  containingClass.getDeclaredField(potentialFieldName);
                  return potentialFieldName;
                } catch (NoSuchFieldException e) {
                  // do nothing and fall through
                }
              }
          
              return super.nameForSetterMethod(config, method, defaultName);
            }
          
            @Override
            public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
            {
              if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
                  && method.getName().startsWith("is")) {
          
                Class<?> containingClass = method.getDeclaringClass();
                String potentialFieldName = method.getName();
          
                try {
                  containingClass.getDeclaredField(potentialFieldName);
                  return potentialFieldName;
                } catch (NoSuchFieldException e) {
                  // do nothing and fall through
                }
              }
              return super.nameForGetterMethod(config, method, defaultName);
            }
          });
          

          基本上它的作用是,在序列化和反序列化之前,它会在目标/源类中检查该类中存在哪些属性名称,无论是isEnabled 还是enabled 属性。

          基于此,映射器将序列化和反序列化为存在的属性名称。

          【讨论】:

          【解决方案10】:

          您可以将原始布尔值更改为 java.lang.Boolean(+ 使用 @JsonPropery

          @JsonProperty("isA")
          private Boolean isA = false;
          
          public Boolean getA() {
              return this.isA;
          }
          
          public void setA(Boolean a) {
              this.isA = a;
          }
          

          对我来说效果很好。

          【讨论】:

          • 这个工作正常。但是,使用 boolean 而不是 Boolean 对我来说效果很好。 +1
          【解决方案11】:

          如果您有兴趣处理不受您控制的第 3 方类(如评论中提到的@edmundpie),那么您将 Mixin 类添加到您的 ObjectMapper 中,其中属性/字段名称应与您的第 3 方类中的名称匹配:

          public class MyStack32270422 {
          
            public static void main(String[] args) {
              ObjectMapper om3rdParty = new ObjectMapper();
              om3rdParty .addMixIn(My3rdPartyResponse.class, MixinMyResponse.class);
              // add further mixins if required
              String jsonString = om3rdParty.writeValueAsString(new My3rdPartyResponse());
              System.out.println(jsonString);
            }
          }
          
          class MixinMyResponse {
            // add all jackson annotations here you want to be used when handling My3rdPartyResponse classes
            @JsonProperty("isSuccess")
            private boolean isSuccess;
          }
          
          class My3rdPartyResponse{
            private boolean isSuccess = true;
            // getter and setter here if desired
          }
          

          基本上,您将所有 Jackson 注释添加到您的 Mixin 类中,就好像您拥有该类一样。在我看来,这是一个很好的解决方案,因为您不必检查以“is..”开头的方法名称等等。

          【讨论】:

            猜你喜欢
            • 2023-04-06
            • 1970-01-01
            • 1970-01-01
            • 2012-07-30
            • 1970-01-01
            • 2013-08-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多