【问题标题】:Jackson adds backslash in json杰克逊在 json 中添加反斜杠
【发布时间】:2017-06-08 12:31:20
【问题描述】:

我正在 Jersey 上构建 REST 服务,并使用 Jackson 从我模型的 java 类生成 JSON。具有绝对简单值的模型,我认为这是最典型的情况。但我得到了奇怪的结果:

[{\"name\":\"Nick\",\"role\":\"admin\",\"age\":\"32\",\"rating\":47}]

我的预期结果:

[{"name":"Nick","role":"admin","age":"32","rating":47}]

我的字段源值不包含任何特殊字符。这些都是简单的词。

这是我的 Java 类。 实体:

public class User {

  private String name;

  private String role;

  private String age;

  private Integer rating;

休息服务等级:

@ServiceConfig(contextName = "myContext")
@Path("/myrest")
public class MyRestService {

  private static final String JSON_CONTENT_TYPE = MediaType.APPLICATION_JSON + ";charset=UTF-8";

  @Context
  protected HttpServletResponse response;

  @GET
  @Path("/users")
  @OpenTransaction
  @Produces({MediaType.APPLICATION_JSON})
  public String findUsers(@QueryParam("department") String department) {

    response.setContentType(JSON_CONTENT_TYPE);
    PDTResponse.status(response).sendStatus(Response.Status.OK.getStatusCode());

    List<User> users = new ArrayList<>();
    users.add(new User("Nick", "admin", "32", 47));

    String jsonInString;
    ObjectMapper mapper = new ObjectMapper();
    try {
        jsonInString = mapper.writeValueAsString(users);
    } catch (JsonProcessingException ex) {
        jsonInString = "thrown exception: " + ex.getMessage();
    }
    return jsonInString;
}

我尝试将注解@JsonRawValue 用于字符串属性:

@JsonRawValue
private String name;

但这种情况下的结果是:

[{\"name\":Nick,\"role\":admin,\"age\":32,\"rating\":47}]

我期望:

[{"name":"Nick","role":"admin","age":"32","rating":47}]

很明显,Jackson 以某种方式逃避了响应结果 json 中的引号。但它为什么会这样做,最重要的是如何避免呢?它们本身只是字符串!没有任何引号或特殊字符。

我使用Java 7Jackson 2.6.1。和Postman 测试结果。 有什么想法可以解决我的问题吗?

【问题讨论】:

  • 当您将一个类似于预期输出的字符串放入一个字符串变量时,引号必须被转义,否则它将不是一个有效的字符串。您想做什么以使带有转义引号的 JSON 字符串不够?
  • 我希望它会受到杰克逊的监管。但我试过return jsonInString.replaceAll("\\\\", "")。它不起作用,我得到相同的结果。
  • 您的 JAX-RS 资源类看起来比应有的复杂。请查看我的answer 了解更多详情。
  • 更好的处理方式。请点击链接:stackoverflow.com/questions/64490278/…

标签: java json rest jersey jackson


【解决方案1】:

您可以配置 ObjectMapper:

final ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
String jsonUsers = mapper.writeValueAsString(users);

更多信息here

【讨论】:

  • 这是一个好主意,但它对我来说并不合适。我得到结果 json "[{name:\"Nick\"...,但我需要 [{"name":"Nick"...。我尝试使用这些设置参数,但没有找到有效的组合。
  • 谢谢,我在 Google 中发现了这个问题,试图允许在 jackson 中使用不带引号的字段名称,而您的回答救了我。
【解决方案2】:

java 中的所有字符串都必须转义其中的引号。所以 jsonInString 应该有斜线。当你输出 jsonInString 虽然它不应该有引号。您是在调试器中查看它吗?

【讨论】:

  • 是的,当然!但是为什么杰克逊不明白?我在调试中看到了简单的字符串[{"name":"Nick",...。但在 Postman 中,我得到的结果为 [{\"name\":\"Nick\",...。据我所知,调试中的值包含反斜杠以更正调试中的显示字符串。但我需要用它做什么?真正的字符串不包含反斜杠。我需要在结果字符串中替换它们吗?我认为这很愚蠢,并且不起作用:return jsonInString.replaceAll("\\\\", "")
【解决方案3】:

这样做。

ObjectMapper mapper = new ObjectMapper();
mapper.getFactory().setCharacterEscapes(new JsonUtil().new CustomCharacterEscapes());
ObjectWriter writer = mapper.writer();

String jsonDataObject = mapper.writeValueAsString(configMap);       

public class CustomCharacterEscapes extends CharacterEscapes {

private final int[] _asciiEscapes;

    public CustomCharacterEscapes() {
       _asciiEscapes = standardAsciiEscapesForJSON();
       //By default the ascii Escape table in jackson has " added as escape string
       //overwriting that here.
       _asciiEscapes['"'] = CharacterEscapes.ESCAPE_NONE;
     }

     @Override
     public int[] getEscapeCodesForAscii() {
       return _asciiEscapes;
     }

     @Override
     public SerializableString getEscapeSequence(int i) {
       return null;
    }
  }

【讨论】:

  • 这应该是答案
【解决方案4】:

看起来您的 JAX-RS 资源类过于复杂了。

要将 Jackson 用作 Jersey 2.x 的 JSON 提供程序,您不需要像这样创建 ObjectMapper 实例。有更好的方法来实现它。继续阅读以了解更多详情。

添加 Jackson 模块依赖项

要将 Jackson 2.x 用作您的 JSON 提供程序,您需要将 jersey-media-json-jackson 模块添加到您的 pom.xml 文件中:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.25.1</version>
</dependency>

注册 Jackson 模块

然后在您的Application / ResourceConfig 子类中注册JacksonFeature

@ApplicationPath("/api")
public class MyApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(JacksonFeature.class);
        return classes;
    }
}
@ApplicationPath("/api")
public class MyApplication extends ResourceConfig {

    public MyApplication() {
        register(JacksonFeature.class);
    }
}

如果您没有 Application / ResourceConfig 子类,则可以在 web.xml 部署描述符中注册 JacksonFeature。可以在jersey.config.server.provider.classnames 初始化参数的逗号分隔值中提供特定的资源、提供者和功能完全限定的类名。

<init-param>
    <param-name>jersey.config.server.provider.classnames</param-name>
    <param-value>org.glassfish.jersey.jackson.JacksonFeature</param-value>
</init-param>

Jackson 提供的MessageBodyWriterJacksonJsonProvider。有关如何使用 Jackson 作为 JSON 提供程序的更多详细信息,请查看answer。如需自定义ObjectMapper,请参考此answer

修复资源类

通过使用上述方法,您的资源类可以很简单:

@Path("/users")
public class MyRestService {

  @GET
  @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"})
  public List<User> findUsers() {

    List<User> users = new ArrayList<>();
    users.add(new User("Nick", "admin", "32", 47));

    return Response.ok(users).build();
}

当请求此类端点时,它将为您提供预期的 JSON 作为结果。

【讨论】:

  • 在这种情况下我得到[{name:\"Nick\..."。但我更改return Response.ok(users).build().toString(); 以避免编译问题。因为方法build() 返回Response。另一件事:我使用Jersey 1.13。这是由对其他模块的依赖关系决定的。切换到第二版会解决这个问题吗?
  • @Kessler Jersey 1.x 已经很老了。如果可以,请升级到 Jersey 2.x。如果无法升级,请查看Jersey 1.x documentation about JSON providers
【解决方案5】:

如果您使用 Spring 和 JSONP 的 @ControllerAdvice,则为 JSON 字符串创建一个包装器并在属性上使用 @JsonRawValue。 JSONP @ControllerAdvice 不会包装字符串响应,它需要一个对象。

public class JsonStringResponse {    
    @JsonValue
    @JsonRawValue
    private String value;

    public JsonStringResponse(String value) {
        this.value = value;
    }
}  

@GetMapping
public ResponseEntity<JsonStringResponse> getJson() {
    String json = "{"id":2}";
    return ResponseEntity.ok().body(new JsonStringResponse(json));
}


@ControllerAdvice
public class JsonpControllerAdvice extends AbstractJsonpResponseBodyAdvice {
    public JsonpControllerAdvice() {
        super("callback");
    }
}

响应是一个json对象{"id":2}
如果有回调参数,则响应为callbackparameter({"id":2});

【讨论】:

    【解决方案6】:

    我也有同样的问题并尝试了不同的解决方案,但都不起作用。问题不在于映射器,而在于映射器的输入。与您的情况一样:
    jsonInString = mapper.writeValueAsString(users);
    'users' 是一个集合。您需要将每个用户转换为JSONObject,将其添加到JSONArray,然后在数组上使用映射器:像这样
    JSONArray users = new JSONArray(); for (Collection user : usersCollection) { JSONObject user = new JSONObject(mapper.writeValueAsString(user)); users.put(user); } mapper.writeValueAsString(user));

    【讨论】:

    • 这是给我的,谢谢!自从我使用杰克逊以来已经有一段时间了,但是当您将 json 转换为字符串时(如在mapper.writeValueAsString 中),java 会转义特殊字符,正如其他人所说。但需要的是记录器输出中的真实 json - 因此,正如 bahadar ali 所说,转换为 jsonnode,然后记录 jsonnnode 对象本身。
    【解决方案7】:

    我不知道为什么,但在我的情况下它可以这样做:

    private static final String COOKIE_TEMPLATE = "{0}={1};Version={2};Domain={3};Max-Age={4};Path='/'";
    
    response.addHeader("Set-Cookie", MessageFormat.format(COOKIE_TEMPLATE, cookie.getName(),cookie.getValue(), cookie.getVersion(), cookie.getDomain(),Integer.toString(cookie.getMaxAge())));
    return ResponseEntity.ok(...);
    

    cookie 是一个 javax.servlet.http.Cookie,而 cookie.getValue() 包含一个由

    产生的字符串
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(obj);
    

    如果我使用

    response.addCookie(cookie)
    

    我将生成的 cookie 定义为带有反斜杠的 JSON。

    但是,如果我使用

    response.addHeader("Set-Cookie",MessageFormat(TEMPLATE,cookie.get...))
    

    我管理的结果 cookie 定义与 JSON 相同,但没有反斜杠。

    如果有多个 cookie,addHeader("Set-Cookie") 只会创建/更新所需的 cookie。其他的保持不变。

    【讨论】:

      【解决方案8】:
      public class StateDate{
          @JsonRawValue
          Boolean state;
          @JsonRawValue
          String date;
      
          public String toJson() {
              ObjectMapper mapper = new ObjectMapper();
              mapper.configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false);
              try {
                  return mapper.writeValueAsString(this);
              } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
                  e.printStackTrace();
              }
              return null;
          }
      
      }
      

      【讨论】:

      • 它只对字段的名称起作用,导致 "name":\"nick\"
      【解决方案9】:

      我也遇到过类似的问题,以下配置将有助于解决问题:

      final ObjectMapper mapper = new ObjectMapper();
      mapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, false);
      

      【讨论】:

      • JsonParser 来自哪里?进口?
      【解决方案10】:

      对于一些在挣扎中仍然需要答案的人

      尝试将@JsonRawValue 添加到该字段。

      @JsonRawValue 注解可以指示 Jackson 完全按原样序列化属性。

      【讨论】:

        【解决方案11】:

        应该没问题,只需要在javascript中解析并使用即可:JSON.parse(response)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-07
          • 2016-06-14
          • 1970-01-01
          • 1970-01-01
          • 2018-08-10
          • 1970-01-01
          • 2019-05-02
          • 2014-02-12
          相关资源
          最近更新 更多