【问题标题】:How to prevent ObjectMapper from converting escaped unicode?如何防止 ObjectMapper 转换转义的 unicode?
【发布时间】:2017-10-04 20:16:45
【问题描述】:

我在 Java 中使用 Jackson 2.4 来做一些 JSON 工作。我使用 Apache HttpGet 调用远程服务器,使用 Jackson 将结果反序列化为 POJO,操作这些结果,然后使用 Jackson 序列化它们以使用 HttpPost 推回远程服务器。

我发现的问题是 Jackson 正在将 unicode 文字转换为 unicode 字符,由于两端的编码问题,我不需要这样做。例如,我可能在 JSON 中有这个:

"field1": "\u00a2"

但是 Jackson 在反序列化时将“\u00a2”转换为“¢”,这会导致远程服务器出现问题。它必须保持为转义的 unicode。如果我使用 Apache EntityUtils(指定 UTF-8)之类的东西,甚至从我的 Web 浏览器调用来获取数据,转义的 unicode 会被保留,所以我知道它是从服务器正确输入的。如果我让 Jackson 在响应中使用来自实体的输入流,它会自动进行转换。

我尝试使用明确设置为 UTF-8 的 JsonGenerator 写入 HttpPost。它没有用,远程服务器仍然拒绝它。我已经研究了 ObjectMapper 和 JsonParser 的配置选项,但我没有看到任何会覆盖此行为的东西。转义非 ASCII,当然,但这不是我需要在这里做的。也许我遗漏了一些明显的东西,但我无法让 Jackson 在不替换转义的 unicode 的情况下反序列化这个字符串。

编辑:好吧,我的错,唯一有问题的文字有 3 或 5 个前导斜杠,而不仅仅是一个。这有点麻烦,但 Java 似乎是在反序列化过程中默认解压它的东西,即使从服务器返回的原始文本保留了它。仍然不确定如何让 Java 在不检查大量文本的情况下保留它。

【问题讨论】:

  • 你的反序列化是如何定义的?
  • 只是为了避免最明显的问题:如果"\u00a2" 是您的Java 代码的一部分,那么Java 编译器正在执行转换而不是Jackson。如果要标注由 6 个字符组成的字符串,则必须使用 "\\u00a2"
  • 您确定要使用正确的字符集发送 JSON 吗?您如何从ObjectMapper 获取 JSON 文本以及该文本如何发送到远程服务器?您是否在该传输中将其编码为 UTF-8?您是否告诉远程服务器您发送的字符集是什么? --- 但是,如果远程服务器无法处理 UTF-8 或您实际使用的任何字符集,并且您想坚持使用 US-ASCII 和 JSON 转义符,请让 ObjectMapper 将 JSON 文本输出为String,然后在发送之前将非ASCII转换为转义。

标签: java json jackson objectmapper


【解决方案1】:

您所期望的超出了 Jackosn 的范围。它是在读取字符串时转换字符串的java。出于同样的原因,如果您有一个值为 \u00a2 的属性文件并使用 jdk API 读取它,您将获得转换后的值。根据文件大小,您可以在将字符串传递给 Json 之前使用双重转义字符 \ 或使用反序列化器(仅适用于字符串)将字符串“转义”回来,如下所示:

谢谢

package com.test.json;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.Map;

public class Jackson {

    static ObjectMapper _MAPPER = new ObjectMapper();

    public static void main(String[] args) throws Exception {
        String json = "{\"field1\": \"\\u00a2\",\"field2\": \"\\u00a2 this\",\"numberField\": 121212}";
        SimpleModule testModule
                = new SimpleModule("StOvFl", _MAPPER.version()).addDeserializer(String.class,
                        new UnEscapedSerializaer());

        _MAPPER.registerModule(testModule);

        Map m = _MAPPER.readValue(json, new TypeReference<Map<String, Object>>() {
        });
        System.out.println("m" + m);

    }
}

class UnEscapedSerializaer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        String s = jp.getValueAsString();
        return org.apache.commons.lang.StringEscapeUtils.StringEscapeUtils.escapeJava(s);

    }
}

【讨论】:

    【解决方案2】:

    自定义 Jackson 行为的另一种方法是自定义 JsonParser。见jackson的JsonFactoryReaderBasedJsonParser的源码;

    关键方法是_finishString2(),用来做'decodeEscaped',所以我们可以写一个JsonParser extends ReaderBasedJsonParser 并覆盖_finishString2 方法:

    public class MyJsonParser extends ReaderBasedJsonParser {
        @Override
        protected void _finishString2() throws IOException {
            char[] outBuf = _textBuffer.getCurrentSegment();
            int outPtr = _textBuffer.getCurrentSegmentSize();
            final int[] codes = _icLatin1;
            final int maxCode = codes.length;
    
            while (true) {
                if (_inputPtr >= _inputEnd) {
                    if (!loadMore()) {
                        _reportInvalidEOF(": was expecting closing quote for a string value");
                    }
                }
                char c = _inputBuffer[_inputPtr++];
                int i = (int) c;
                if (i < maxCode && codes[i] != 0) {
                    if (i == INT_QUOTE) {
                        break;
                    } else {
                        //c = _decodeEscaped();
                        //do nth
                    }
                }
                // Need more room?
                if (outPtr >= outBuf.length) {
                    outBuf = _textBuffer.finishCurrentSegment();
                    outPtr = 0;
                }
                // Ok, let's add char to output:
                outBuf[outPtr++] = c;
            }
            _textBuffer.setCurrentLength(outPtr);
        }
    
        public static void main(String[] args) throws IOException {
            String json = "{\"field1\": \"\\u00a2\",\"field2\": \"\\u00a2 this\",\"numberField\": 121212}";
            ObjectMapper objectMapper = new ObjectMapper(new MyJsonParserFactory());
            Object o = objectMapper.readValue(json, Object.class);
            System.out.println(o);
        }
    }
    

    完整的演示代码here

    【讨论】:

      猜你喜欢
      • 2020-03-27
      • 2011-09-23
      • 1970-01-01
      • 2010-11-06
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-09
      相关资源
      最近更新 更多