将JSON源代码转换为Java对象

3

我有一个包含json数据的URL(https://),但它受到了称为JSON劫持攻击的保护,因为源代码以以下方式开头: )]}', 并且源代码如下:

  [
  {
    "address": {
        "street": "Neuenfelder Str",
        "housenumber": "13A",
        "postalcode": "21109",
        "city": "Hamburg",
        "geoLocation": {
            "lat": "52.092309",
            "lng": "5.130041"
        }
    },
    "distance": 0
  },
  {
    "address": {
        "street": "Umber Str",
        "housenumber": "2",
        "postalcode": "22567",
        "city": "Berlin",
        "geoLocation": {
            "lat": "51.5761166",
            "lng": "5.0377286"
        }
    },
    "distance": 0
  },
  .....]

我尝试通过使用 Jacksongson 甚至 org.json 创建客户端代码并消耗这个 json,但我遇到了解析错误:

无法读取文档: 预期之中的字符(')' (代码 41)): 预期是一个有效值(数字、字符串、数组、对象、'true'、'false' 或 'null')

我可以将 json 转换为 Java 对象,方法是将其保存为文件并删除这些字符 )]}'
但我需要知道是否有一种方式可以将此源代码转换为我们通常使用的正常方式,而不必将其保存为文档并只使用这些库 (ObjectMapperResponseEntity)。

1
将此源代码作为字符串输入,并删除开头的字符 )]}' - Sachin Gupta
2
听起来问题不是如何将JSON转换为Java,而是如何修复损坏的JSON片段。 - RobCo
1
为什么你想要一种不去除无效字符的方法?这就像问,“1 + 1是多少?”但不接受2作为答案一样。 - Wietlol
1
您必须剥离第一个字符,但由于您没有包括任何有关读取JSON和内容的源代码,因此我们无法帮助您。 - Arne Burmeister
1
如果您可以控制发送JSON的服务器代码,请确保它发送有效的JSON,否则您将不得不修剪它以使其有效。 - thefrugaldev
显示剩余6条评论
1个回答

1
你提到的所有主要JSON库都没有提供解析你所说的JSON文档的方法:它们只能处理格式正确的JSON文档,其他任何情况都不能处理,这是它们的唯一责任。你提到了一个无效的JSON文档,所以这就是它们无法解析的唯一原因。然而,你在问题中提到的所有库都可以透明地使用Reader,并且只需删除那五个字符,你就可以修复损坏的JSON文档(对于JSON标准或任何JSON工具来说,它都是损坏的),从而可以被任何一个库使用。请注意,你不需要任何“特殊”的库(好吧,假设这样的库存在,但如果它缺少你可以与你喜欢的JSON库一起使用的任何功能,那么你会很失望?),因为你可以使其适用于任何一个库。性能提示:为了节省内存并能够处理巨大的JSON文档,你不需要使用字符串。

考虑以下“JSON”文档:

)]}',{"foo":"bar"}

知道最前面的五个字符会阻止任何JSON解析器解析它,你可以轻松地将它们剥离:

private static final char[] protectingCharacters = {
        ')', ']', '}', '\'', ','
};

static Reader stripProtection(final Reader reader)
        throws IOException {
    // Allocating a temporary buffer
    final char[] buffer = new char[protectingCharacters.length];
    // Check if the given Reader instance supports reading ahead, and wrap if necessary
    // This is necessary in order to restore the reader position
    final Reader normalizedReader = reader.markSupported() ? reader : new BufferedReader(reader);
    // Memorizing the current position telling the reader to limit its internal buffer size just with five characters
    normalizedReader.mark(protectingCharacters.length);
    // Reading five characters to the buffer
    // We don't need to check how many characters were read -- we'll check it below
    normalizedReader.read(buffer, 0, protectingCharacters.length);
    // Not a protecting mark?
    if ( !Arrays.equals(protectingCharacters, buffer) ) {
        // Then just rewind the reader pointer to the position stored with the mark() invocation
        normalizedReader.reset();
    }
    // Or assume that skipping five characters is fine
    return normalizedReader;
}

以下示例使用 stripProtection 方法解析给定的输入流,以创建其各自的 JSON 树模型,使示例更加简洁。其中涉及 Gson、Jackson 和 org.json 库。
static void testWithGson()
        throws IOException {
    try ( final Reader reader = stripProtection(getPackageResourceReader(Q42971905.class, "no-hijacking.json")) ) {
        final JsonParser parser = new JsonParser();
        final JsonElement jsonElement = parser.parse(reader);
        System.out.println(jsonElement);
    }
}

static void testWithJackson()
        throws IOException {
    try ( final Reader reader = stripProtection(getPackageResourceReader(Q42971905.class, "no-hijacking.json")) ) {
        final ObjectMapper objectMapper = new ObjectMapper();
        final JsonNode jsonNode = objectMapper.readTree(reader);
        System.out.println(jsonNode);
    }
}

static void testWithOrgJson()
        throws IOException {
    try ( final Reader reader = stripProtection(getPackageResourceReader(Q42971905.class, "no-hijacking.json")) ) {
        final JSONTokener jsonTokener = new JSONTokener(reader);
        final Object value = jsonTokener.nextValue();
        System.out.println(value);
    }
}

所有三种方法都会产生以下输出:

{"foo":"bar"}

如果出于某些原因,您需要生成这样的文档,只需编写如下方法即可:
static Writer coverWithProtection(final Writer writer)
        throws IOException {
    writer.write(protectingCharacters);
    return writer;
}

将编写器传递给您正在使用的相应 JSON 库。此方法只会在编写器输出前添加 )]}',

非常感谢,我需要确认一下。这很详细。我尝试了你的解决方案,jsonNode返回格式良好的JSON文档,并且在性能方面更加合理。 - fiskra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接