fiskra
fiskra

Reputation: 892

convert json source to java object

I have an url (https://) which includes json data but it has a protection against called JSON highjacking exploit I think because the source begins with: )]}', and the source is like:

  [
  {
    "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
  },
  .....]

I tried to create client code and consume this json by using Jackson, gson and even org.json but I got parsing error :

Could not read document: Unexpected character (')' (code 41)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')

I could convert json to Java object by saving as a file and remove these characters )]}'.

But I need to know is there any way to convert this source as a normal way we do without saving as a document and just use these libraries(ObjectMapper or ResponseEntity).

Upvotes: 3

Views: 296

Answers (1)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21105

All major JSON libraries you mentioned do NOT provide any way of parsing JSON documents you are talking about: they can just work with well-formed JSON documents, nothing else, and that's the only and justified their responsibility. You mentioned a document that is not a valid JSON, so that's the only reason of why all of them fail to parse it. However, all of the libraries you mentioned in your question work with Readers transparently, and having just removed those five characters, you can fix the broken JSON document (and it's broken for the JSON standard or any JSON tool) so it could be consumed by any of those library. Please note that you don't need a "special" library of any kind (ok, let's assume such a library exists, but what if it lacks any features that you can use with your favorite JSON library, so that making you sad?), because you can just make it work for any. Performance note: you don't need to use strings in order to save memory and being able to process huge JSON documents as well.

Consider the following "JSON" document:

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

Knowing that the very first five characters prevent any JSON parser to parse it, you can easily just strip them:

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

The following examples use the stripProtection method for the Gson, Jackson and org.json libraries parsing the given input stream to create their respective JSON tree models for simplicity:

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

All three methods produce the following output:

{"foo":"bar"}

If, for some reason, you need to generate such documents, you just have to write a method like this:

static Writer coverWithProtection(final Writer writer)
        throws IOException {
    writer.write(protectingCharacters);
    return writer;
}

And pass the writer to the corresponding JSON library you're using. This method will just prepend the writer output with )]}',.

Upvotes: 1

Related Questions