【问题标题】:Different names of JSON property during serialization and deserialization序列化和反序列化过程中 JSON 属性的不同名称
【发布时间】:2012-01-23 12:36:23
【问题描述】:

是否有可能:类中有一个字段,但在 Jackson 库中的序列化/反序列化过程中有不同的名称?

例如,我有“坐标”类。

class Coordinates{
  int red;
}

对于 JSON 的反序列化,希望具有如下格式:

{
  "red":12
}

但是当我序列化对象时,结果应该是这样的:

{
  "r":12
}

我尝试通过在 getter 和 setter(具有不同的值)上应用 @JsonProperty 注释来实现这一点:

class Coordiantes{
    int red;

    @JsonProperty("r")
    public byte getRed() {
      return red;
    }

    @JsonProperty("red")
    public void setRed(byte red) {
      this.red = red;
    }
}

但我遇到了一个例外:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException:无法识别的字段“红色”

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    刚刚测试过,这行得通:

    public class Coordinates {
        byte red;
    
        @JsonProperty("r")
        public byte getR() {
          return red;
        }
    
        @JsonProperty("red")
        public void setRed(byte red) {
          this.red = red;
        }
    }
    

    这个想法是方法名称应该不同,因此杰克逊将其解析为不同的字段,而不是一个字段。

    这里是测试代码:

    Coordinates c = new Coordinates();
    c.setRed((byte) 5);
    
    ObjectMapper mapper = new ObjectMapper();
    System.out.println("Serialization: " + mapper.writeValueAsString(c));
    
    Coordinates r = mapper.readValue("{\"red\":25}",Coordinates.class);
    System.out.println("Deserialization: " + r.getR());
    

    结果:

    Serialization: {"r":5}
    Deserialization: 25
    

    【讨论】:

    • 与 jaxb 一样吗?
    • 这对Enums 有用吗?
    【解决方案2】:

    您可以使用在 jackson 2.9.0 中引入的@jsonAlias

    例子:

    public class Info {
      @JsonAlias({ "red" })
      public String r;
    }
    

    这在序列化期间使用r,但在反序列化期间允许red 作为别名。不过,这仍然允许 r 被反序列化。

    【讨论】:

    • documentation for @JsonAlias 明确声明它是has no effect during serialization where primary name is always used。这不是 OP 想要的。
    • @XaeroDegreaz 我猜@Asura 的意思是,您可以使用r 作为主要名称,但red 用于@JsonAlias,它允许将其序列化为r,但添加@ 987654332@ 在反序列化时被识别。用@JsonProperty("r")@JsonAlias("red") 注释它应该可以很好地解决给定的问题。
    【解决方案3】:

    您可以使用@JsonSetter@JsonGetter 的组合来分别控制您的属性的反序列化和序列化。这也将允许您保留与您的实际字段名称相对应的标准化 getter 和 setter 方法名称。

    import com.fasterxml.jackson.annotation.JsonSetter;    
    import com.fasterxml.jackson.annotation.JsonGetter;
    
    class Coordinates {
        private int red;
    
        //# Used during serialization
        @JsonGetter("r")
        public int getRed() {
            return red;
        }
    
        //# Used during deserialization
        @JsonSetter("red")
        public void setRed(int red) {
            this.red = red;
        }
    }
    

    【讨论】:

      【解决方案4】:

      我会将两个不同的 getter/setter 对绑定到一个变量:

      class Coordinates{
          int red;
      
          @JsonProperty("red")
          public byte getRed() {
            return red;
          }
      
          public void setRed(byte red) {
            this.red = red;
          }
      
          @JsonProperty("r")
          public byte getR() {
            return red;
          }
      
          public void setR(byte red) {
            this.red = red;
          }
      }
      

      【讨论】:

      • 但是在这种情况下,在序列化过程中,我们将获得两个属性:“r”和“red”,具有相同的值。
      • @JsonIgnore 关于您不想处理的方法将解决该问题
      • 现在有更方便的注解:@JsonGetter@JsonSetter。因此可以准确设置序列化程序的行为方式。
      【解决方案5】:

      可以有正常的 getter/setter 对。您只需要在@JsonProperty中指定访问模式

      这里是单元测试:

      public class JsonPropertyTest {
      
        private static class TestJackson {
      
          private String color;
      
          @JsonProperty(value = "device_color", access = JsonProperty.Access.READ_ONLY)
          public String getColor() {
            return color;
          };
      
          @JsonProperty(value = "color", access = JsonProperty.Access.WRITE_ONLY)
          public void setColor(String color) {
            this.color = color;
          }
      
        }
      
        @Test
        public void shouldParseWithAccessModeSpecified() throws Exception {
          String colorJson = "{\"color\":\"red\"}";
          ObjectMapper mapper = new ObjectMapper();
          TestJackson colotObject = mapper.readValue(colorJson, TestJackson.class);
      
          String ser = mapper.writeValueAsString(colotObject);
          System.out.println("Serialized colotObject: " + ser);
        }
      }
      

      我得到的输出如下:

      Serialized colotObject: {"device_color":"red"}
      

      【讨论】:

      • 以上解决方案对我有用。我正在使用 Spring Rest 4 和 jackson 2.9.10
      【解决方案6】:

      你可以使用这个变体:

      import lombok.Getter;
      import com.fasterxml.jackson.annotation.JsonGetter;
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      //...
      
      @JsonProperty(value = "rr") // for deserialization
      @Getter(onMethod_ = {@JsonGetter(value = "r")}) // for serialization
      private String rrrr;
      

      使用 Lombok 吸气剂

      【讨论】:

        【解决方案7】:

        这不是我所期望的解决方案(尽管它是一个合法的用例)。我的要求是允许现有的错误客户端(已经发布的移动应用程序)使用备用名称。

        解决方案在于提供一个单独的 setter 方法,如下所示:

        @JsonSetter( "r" )
        public void alternateSetRed( byte red ) {
            this.red = red;
        }
        

        【讨论】:

          【解决方案8】:

          使用 Jackson 2.9+ 引入的 @JsonAlias 进行注释,而无需在要反序列化的项目上使用多个别名(json 属性的不同名称)提及 @JsonProperty 工作正常。

          我使用com.fasterxml.jackson.annotation.JsonAliascom.fasterxml.jackson.databind.ObjectMapper 用于我的用例的包一致性。

          例如:

          @Data
          @Builder
          public class Chair {
          
              @JsonAlias({"woodenChair", "steelChair"})
              private String entityType;
          
          }
          
          
          @Test
          public void test1() {
          
             String str1 = "{\"woodenChair\":\"chair made of wood\"}";
             System.out.println( mapper.readValue(str1, Chair.class));
             String str2 = "{\"steelChair\":\"chair made of steel\"}";
             System.out.println( mapper.readValue(str2, Chair.class));
          
          }
          

          工作正常。

          【讨论】:

            【解决方案9】:

            我知道这是一个老问题,但对我来说,当我发现它与 Gson 库冲突时,我就开始工作了,所以如果您使用 Gson,请使用 @SerializedName("name") 而不是 @JsonProperty("name") 希望这会有所帮助

            【讨论】:

              【解决方案10】:

              他们必须将此作为一项功能包含在内,因为现在为 getter 和 setter 设置不同的 @JsonProperty 会产生您所期望的结果(同一字段的序列化和反序列化期间的不同属性名称)。杰克逊版本 2.6.7

              【讨论】:

                【解决方案11】:

                就我而言,我必须阅读巴西葡萄牙语的输入并生成英语输出。

                因此,对我有用的解决方法是使用@JsonAlias 而不是@JsonProperty

                
                // pseudo-java
                @Value
                public class User {
                
                   String username;
                
                   public User(
                      @JsonAlias("nome_usuario") String username) {
                     // ...
                   }
                
                }
                
                

                【讨论】:

                  【解决方案12】:

                  您可以编写一个序列化类来做到这一点:

                  public class Symbol
                  
                  {
                       private String symbol;
                  
                       private String name;
                  
                       public String getSymbol() {
                          return symbol;
                      }
                      public void setSymbol(String symbol) {
                          this.symbol = symbol;
                      }    
                      public String getName() {
                          return name;
                      }    
                      public void setName(String name) {
                          this.name = name;
                      }
                  }
                  
                  public class SymbolJsonSerializer extends JsonSerializer<Symbol> {
                  
                      @Override
                      public void serialize(Symbol symbol, JsonGenerator jgen, SerializerProvider serializers) throws IOException, JsonProcessingException {
                          jgen.writeStartObject();
                  
                          jgen.writeStringField("symbol", symbol.getSymbol());
                           //Changed name to full_name as the field name of Json string
                          jgen.writeStringField("full_name", symbol.getName());
                          jgen.writeEndObject(); 
                      }
                  }
                  
                  
                              ObjectMapper mapper = new ObjectMapper();
                  
                              SimpleModule module = new SimpleModule();
                              module.addSerializer(Symbol.class, new SymbolJsonSerializer());
                              mapper.registerModule(module); 
                  
                              //only convert non-null field, option...
                              mapper.setSerializationInclusion(Include.NON_NULL); 
                  
                              String jsonString = mapper.writeValueAsString(symbolList);
                  
                  

                  【讨论】:

                    猜你喜欢
                    • 2021-07-22
                    • 2013-10-09
                    • 1970-01-01
                    • 2019-12-24
                    • 2014-04-24
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-11-02
                    相关资源
                    最近更新 更多