如何防止ObjectMapper转换转义的unicode?

问题描述 投票:0回答:3

我正在 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 在不检查大量文本的情况下保留它。

java json jackson objectmapper
3个回答
1
投票

您所期望的超出了 Jackosn 的范围。 java在读取字符串的同时对其进行转换。出于同样的原因,如果您有一个值为

\u00a2
的属性文件并使用 jdk API 读取它,您将获得转换后的值。根据文件大小,您可以在将字符串传递给 Json 之前对 char \ 进行双重转义,或者使用反序列化器(仅适用于字符串)将字符串“转义”回来,如下所示:

谢谢你

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);

    }
}

0
投票

自定义 Jackson 行为的另一种方法是自定义

JsonParser
。查看jackson的JsonFactoryReaderBasedJsonParser;

的源代码

关键方法是

_finishString2()
,它用于执行“decodeEscaped”,因此我们可以编写一个JsonParser扩展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);
    }
}

完整演示代码这里


0
投票

花了几个小时寻找解决方案,我找到了。

我有一些二进制数据。例如

0xab 0xa6 0xaa
我希望我的 json 看起来像这样:

{
  "binary-data-as-unicode": "\u00ab\u00a6\u00aa"
}

此 json 的读者将摆脱

\u00
并将剩下的内容视为表示二进制数据的十六进制字符串。

为了使用 jackson ObjectMapper,我用十六进制数据的格式化 unicode 表示法字符串准备了一个 String 对象:

public static String toHexString(ByteArrayOutputStream stream) {
    byte[] byteArray = stream.toByteArray();
    StringBuilder hexString = new StringBuilder();
    for (byte b : byteArray) {
        hexString.append(String.format("\\u%04X", b & 0xFF));
    }
    return String.format("\"%s\"", hexString.toString());
}

然后,我用了:

// stream of type ByteArrayOutputStream
String unicodedString = toHexString(stream);
ObjectNode objNode = mapper.createObjectNode();
objNode.putRawValue("field-name", new RawValue(unicodedString));

这样,我就没有再逃避了,我得到了我想要的东西。

© www.soinside.com 2019 - 2024. All rights reserved.