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

k4ymrczo  于 2023-03-24  发布在  其他
关注(0)|答案(3)|浏览(323)

我在Java中使用Jackson2.4来做一些JSON的跑腿工作。我用Apache HttpGet调用远程服务器,用Jackson将结果反序列化为POJO,操作这些结果,然后用Jackson序列化它们,用HttpPost推回远程服务器。
我发现的问题是Jackson正在将unicode字面量转换为unicode字符,由于两端的编码问题,我不需要它这样做。例如,我可能在JSON中有这样的内容:

"field1": "\u00a2"

但是Jackson在反序列化时将“\u00a2”转换为“TM”,这会导致远程服务器出现问题。它必须作为转义的unicode进行维护。(指定UTF-8)或者甚至从我的Web浏览器调用来获取数据,转义的unicode会被保留,所以我知道它是从服务器正确地进来的。如果我让Jackson从响应上的实体消费输入流,它会自动进行转换。
我试过用JsonGenerator写,它被显式设置为UTF-8以写入HttpPost。它不起作用,远程服务器仍然拒绝它。我已经深入研究了ObjectMapper和JsonParser的配置选项,但我没有看到任何可以覆盖这种行为的东西。当然,转义非ASCII,但这不是我在这里需要做的。也许我错过了一些明显的东西,但是我不能让Jackson反序列化这个字符串而不替换转义的unicode。
编辑:好吧,我的错,唯一有问题的字面量有3或5个前导斜杠,而不是一个。这有点奇怪,但Java似乎是在反序列化过程中默认解包的,即使从服务器返回的原始文本保留了它。仍然不知道如何让Java在不检查疯狂数量的文本的情况下保留它。

2sbarzqh

2sbarzqh1#

你所期望的是在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);

    }
}
0ejtzxu1

0ejtzxu12#

另一种自定义Jackson行为的方法是自定义JsonParser
关键的方法是_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);
    }
}

完整的演示代码here

igsr9ssn

igsr9ssn3#

我花了几个小时想办法,终于找到了。
我有一些二进制数据。例如0xab 0xa6 0xaa我想我的json看起来像这样:

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

这个json的读取器将去掉\u00,并将剩下的内容视为表示二进制数据的十六进制字符串。
为了使用JacksonObjectMapper,我准备了一个String对象,其中包含十六进制数据的格式化unicode符号字符串:

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

这样一来,我就再也逃不掉了,我得到了我想要的。

相关问题