There's the javax.json.JSONParser.

An example that prints out a JSON:

import javax.json.Json;
import javax.json.stream.JsonParser;
...
JsonParser parser = Json.createParser(new StringReader(jsonData));
while (parser.hasNext()) {
   JsonParser.Event event = parser.next();
   switch(event) {
      case START_ARRAY:
      case END_ARRAY:
      case START_OBJECT:
      case END_OBJECT:
      case VALUE_FALSE:
      case VALUE_NULL:
      case VALUE_TRUE:
         System.out.println(event.toString());
         break;
      case KEY_NAME:
         System.out.print(event.toString() + " " +
                          parser.getString() + " - ");
         break;
      case VALUE_STRING:
      case VALUE_NUMBER:
         System.out.println(event.toString() + " " +
                            parser.getString());
         break;
   }
}

from The Java EE Tutorial 19.4.1 Reading JSON Data Using a Parser. It's part of the JavaEE 7 API but a standalone jar can be gotten from https://jsonp.java.net/download.html.

Answer from vandale on Stack Overflow
🌐
TutorialsPoint
tutorialspoint.com › how-to-parse-a-json-string-using-streaming-api-in-java
How to parse a JSON string using Streaming API in Java?
We can parse a JSON using the static method createParser() of Json class. public static JsonParser createParser(Reader reader) import java.io.*; import javax.json.Json; import javax.json.stream.JsonParser; import javax.json.stream.JsonParser.Event; public class JSONParseringTest { public static ...
🌐
JAXB
javaee.github.io › tutorial › jsonp004.html
Using the Streaming API - Java EE
The following code demonstrates how to create a JsonParser object and how to parse JSON data using events: import javax.json.Json; import javax.json.stream.JsonParser; ... JsonParser parser = Json.createParser(new StringReader(jsonData)); while (parser.hasNext()) { JsonParser.Event event = parser.next(); switch(event) { case START_ARRAY: case END_ARRAY: case START_OBJECT: case END_OBJECT: case VALUE_FALSE: case VALUE_NULL: case VALUE_TRUE: System.out.println(event.toString()); break; case KEY_NAME: System.out.print(event.toString() + " " + parser.getString() + " - "); break; case VALUE_STRING: case VALUE_NUMBER: System.out.println(event.toString() + " " + parser.getString()); break; } }
🌐
Baeldung
baeldung.com › home › json › jackson › jackson streaming api
Jackson Streaming API | Baeldung
March 26, 2025 - Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time. ... In this article, we will be looking at the Jackson Streaming API. It supports both reading and writing, and by using it, we can write high-performance and fast JSON parsers.
🌐
Medium
medium.com › swlh › understanding-the-power-of-streams-in-java-847efd092ddf
Understanding the Power of Streams in Java | by masooria | The Startup | Medium
November 3, 2020 - In this case given the Stream of Json Records by line 2 line 3 gets the JsonArray “Resources” for each record converts it into another stream. By this time we have a stream<stream<JSONObject>> So the flatmap converts (flattens) it to stream<JSONObject> (java flatmap only works or only flattens nested streams not just any iterable)
🌐
Northconcepts
northconcepts.com › docs › examples › read-a-json-stream
Read a JSON Stream - Data Pipeline
June 2, 2023 - This example shows you how to create a JSON stream in Java using the JsonReader class.
🌐
HowToDoInJava
howtodoinjava.com › home › gson › gson jsonreader
Gson JsonReader - Streaming JSON Parser - HowToDoInJava
April 3, 2023 - It helps read a JSON (RFC 7159) encoded value as a stream of tokens. It reads both literal values (strings, numbers, booleans, and nulls) as well as the begin and end delimiters of objects and arrays.
🌐
GitHub
gist.github.com › d7e32e0903d00140b95e24523c10aa7c
Java Sample to read JSon and parse using Stream API · GitHub
Java Sample to read JSon and parse using Stream API · Raw · MainPetsListing.java · This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Find elsewhere
🌐
GitHub
github.com › pwall567 › json-stream
GitHub - pwall567/json-stream: JSON Streaming library for Java
This class takes an Acceptor as a constructor argument (see pipelines), and as characters are fed to it from the string form of a JSON array, the parsed array elements are passed to the consumer. See the test for an example. The latest version of the library is 0.9, and it may be obtained from the Maven Central repository. <dependency> <groupId>net.pwall.json</groupId> <artifactId>json-stream</artifactId> <version>0.9</version> </dependency>
Author: pwall567
Top answer
1 of 11
78

I would suggest you have to use a Reader to convert your InputStream in.

BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8")); 
StringBuilder responseStrBuilder = new StringBuilder();

String inputStr;
while ((inputStr = streamReader.readLine()) != null)
    responseStrBuilder.append(inputStr);
new JSONObject(responseStrBuilder.toString());

I tried in.toString() but it returns:

getClass().getName() + '@' + Integer.toHexString(hashCode())

(like documentation says it derives to toString from Object)

2 of 11
40

All the current answers assume that it is okay to pull the entire JSON into memory where the advantage of an InputStream is that you can read the input little by little. If you would like to avoid reading the entire Json file at once then I would suggest using the Jackson library (which is my personal favorite but I'm sure others like Gson have similar functions).

With Jackson you can use a JsonParser to read one section at a time. Below is an example of code I wrote that wraps the reading of an Array of JsonObjects in an Iterator. If you just want to see an example of Jackson, look at the initJsonParser, initFirstElement, and initNextObject methods.

public class JsonObjectIterator implements Iterator<Map<String, Object>>, Closeable {
    private static final Logger LOG = LoggerFactory.getLogger(JsonObjectIterator.class);

    private final InputStream inputStream;
    private JsonParser jsonParser;
    private boolean isInitialized;

    private Map<String, Object> nextObject;

    public JsonObjectIterator(final InputStream inputStream) {
        this.inputStream = inputStream;
        this.isInitialized = false;
        this.nextObject = null;
    }

    private void init() {
        this.initJsonParser();
        this.initFirstElement();
        this.isInitialized = true;
    }

    private void initJsonParser() {
        final ObjectMapper objectMapper = new ObjectMapper();
        final JsonFactory jsonFactory = objectMapper.getFactory();

        try {
            this.jsonParser = jsonFactory.createParser(inputStream);
        } catch (final IOException e) {
            LOG.error("There was a problem setting up the JsonParser: " + e.getMessage(), e);
            throw new RuntimeException("There was a problem setting up the JsonParser: " + e.getMessage(), e);
        }
    }

    private void initFirstElement() {
        try {
            // Check that the first element is the start of an array
            final JsonToken arrayStartToken = this.jsonParser.nextToken();
            if (arrayStartToken != JsonToken.START_ARRAY) {
                throw new IllegalStateException("The first element of the Json structure was expected to be a start array token, but it was: " + arrayStartToken);
            }

            // Initialize the first object
            this.initNextObject();
        } catch (final Exception e) {
            LOG.error("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
            throw new RuntimeException("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
        }

    }

    private void initNextObject() {
        try {
            final JsonToken nextToken = this.jsonParser.nextToken();

            // Check for the end of the array which will mean we're done
            if (nextToken == JsonToken.END_ARRAY) {
                this.nextObject = null;
                return;
            }

            // Make sure the next token is the start of an object
            if (nextToken != JsonToken.START_OBJECT) {
                throw new IllegalStateException("The next token of Json structure was expected to be a start object token, but it was: " + nextToken);
            }

            // Get the next product and make sure it's not null
            this.nextObject = this.jsonParser.readValueAs(new TypeReference<Map<String, Object>>() { });
            if (this.nextObject == null) {
                throw new IllegalStateException("The next parsed object of the Json structure was null");
            }
        } catch (final Exception e) {
            LOG.error("There was a problem initializing the next Object: " + e.getMessage(), e);
            throw new RuntimeException("There was a problem initializing the next Object: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean hasNext() {
        if (!this.isInitialized) {
            this.init();
        }

        return this.nextObject != null;
    }

    @Override
    public Map<String, Object> next() {
        // This method will return the current object and initialize the next object so hasNext will always have knowledge of the current state

        // Makes sure we're initialized first
        if (!this.isInitialized) {
            this.init();
        }

        // Store the current next object for return
        final Map<String, Object> currentNextObject = this.nextObject;

        // Initialize the next object
        this.initNextObject();

        return currentNextObject;
    }

    @Override
    public void close() throws IOException {
        IOUtils.closeQuietly(this.jsonParser);
        IOUtils.closeQuietly(this.inputStream);
    }

}

If you don't care about memory usage, then it would certainly be easier to read the entire file and parse it as one big Json as mentioned in other answers.

🌐
Mkyong
mkyong.com › home › java › gson streaming apis to read and write json
Gson Streaming APIs to read and write JSON | mkyong.com
May 8, 2024 - java August 11, 2011 by mkyong 0 comments · Streaming APIs are efficient ways of processing large JSON files or data without loading the entire document into memory. Gson provides JsonReader and JsonWriter classes for JSON streaming. Gson’s Streaming API · JsonWriter – Write JSON as a stream. JsonReader – Read JSON as a stream. Table of contents: 1. Setup Google Gson · 1. Write JSON String using JsonWriter ·
🌐
JAXB
javaee.github.io › javaee-spec › javadocs › javax › json › stream › JsonParser.html
JsonParser (Java(TM) EE 8 Specification APIs)
Provides forward, read-only access to JSON data in a streaming way. This is the most efficient way for reading JSON data. This is the only way to parse and process JSON data that are too big to be loaded in memory. The class Json contains methods to create parsers from input sources (InputStream and Reader). The following example demonstrates how to create a parser from a string ...
Top answer
1 of 1
3

Yes, you can use the methods that don't load the whole JSON into the memory using JsonParser:

  • use JsonParser.readBinaryValue(OutputStream o)
  • use JsonParser.getText(Writer w)
    • inherit the relevant factory and JsonParser implementation classes and tweak them to your needs
  • inherit the writer/stream class and write your own implementation

Unfortunately, using another library GSON, there's no way to use a writer stream because its interface only provides a nextString method.

Using readBinaryValue

you can use another method that reads base64, decodes it to binary and streams it to your own OutputStream.

parser.readBinaryValue(Base64Variants.MIME, jsonOutputStream);

and then inherit and tweak the implementation of your OutputStream to use it the way you want

public class JsonOutputStream extends OutputStream {
    private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(4);
    private final Base64.Encoder encoder = Base64.getMimeEncoder();

    @Getter
    private int count = 0;

    @Override
    public void write(int b) throws IOException {
        buffer.write(b);
        if (buffer.size() % 4 == 0) { // Process in chunks of 4 Base64 characters
            flushBuffer();
        }
    }

    public void resetCount() {
        count = 0;
    }

    private void flushBuffer() {
        if (buffer.size() < 4) return; // Only decode full Base64 chunks
        byte[] encoded = encoder.encode(buffer.toByteArray());
        buffer.reset();
    }
}

JsonParser uses its own base64 variant, if you want the standard one you have to choose MIME. Another option is also URL.

And the good thing about this option is that you don't have to tweak anything in this case as it doesn't create segments like the getText option. It sets the buffer length configured more or less like private final static int[] BYTE_BUFFER_LENGTHS = new int[] { 8000, 8000, 2000, 2000 }; (because there's a padding in b64, and chunks are always 4 bytes long, there is some tweaking with - 3). Then when it needs more room, it simply flushes the buffer.

Therefore, it doesn't load the whole string into the memory and you don't have to do any custom tweaking.

Using JsonParser.getText(Writer w)

The basic JsonParser.getText(writer) does a .getText which loads everything into memory

Yes, you're right, but you can tweak it. Every implementation like ReaderBasedJsonParser or UTF8DataInputJsonParser calls a helper method inside the getText from the snippet

if (t == JsonToken.VALUE_STRING) {
    if (_tokenIncomplete) {
    _tokenIncomplete = false;
    _finishString(); // only strings can be incomplete
  }

and I debugged it, for the string tokens _tokenIncomplete is always true as intended. Then inside the method _finishedString2 called by the _finishString there is a nice loop that copies the JSON's string input into the buffer that is segmented. Each segment has the size that can be configured from the factory in the context passed to the parser's constructor. The default value configured in TextBuffer is

final static int MAX_SEGMENT_LEN = 0x10000;

and the new segment is created in the code fragment

// Need more room?
if (outPtr >= outBuf.length) {
    outBuf = _textBuffer.finishCurrentSegment();
    outPtr = 0;
}

so instead of finishing the current segment we can simply write the contents to our writer and reset the buffer, releasing the memory each time a new chunk is read. That way we never load the whole string into the memory, only MAX_SEGMENT_LEN at maximum.

For example, you can inherit the ReaderJsonParser and write your own factory method for it analogously to the JsonFactory:

public class ChunkReaderBasedJsonParser extends ReaderBasedJsonParser {
    private boolean finishedStringBuffer = false;

    public ChunkReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st, char[] inputBuffer, int start, int end, boolean bufferRecyclable) {
        super(ctxt, features, r, codec, st, inputBuffer, start, end, bufferRecyclable);
    }

    public ChunkReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st) {
        super(ctxt, features, r, codec, st);
    }

    @Override
    public int getText(Writer writer) throws IOException {
        JsonToken t = _currToken;
        if (t == JsonToken.VALUE_STRING) {
            int total = 0;
            while (_tokenIncomplete) {
                _tokenIncomplete = getNextStringChunk();
                total += _textBuffer.contentsToWriter(writer);
                _textBuffer.resetWithEmpty();
            }
            return total;
        }
        if (t == JsonToken.FIELD_NAME) {
            String n = _parsingContext.getCurrentName();
            writer.write(n);
            return n.length();
        }
        if (t != null) {
            if (t.isNumeric()) {
                return _textBuffer.contentsToWriter(writer);
            }
            char[] ch = t.asCharArray();
            writer.write(ch);
            return ch.length;
        }
        return 0;
    }

    /**
     *
     * @return true if the token is still incomplete, false if it is complete
     */
    private boolean getNextStringChunk() throws IOException {
        finishedStringBuffer = false;
        _finishString();
        return finishedStringBuffer;
    }

    @Override
    protected void _finishString2() throws IOException {
        char[] outBuf = _textBuffer.getCurrentSegment();
        int outPtr = _textBuffer.getCurrentSegmentSize();
        final int[] codes = INPUT_CODES_LATIN1;
        final int maxCode = codes.length;

        while (true) {
            // we ran out of buffer?
            if (outPtr >= outBuf.length) {
                finishedStringBuffer = true;
                return;
            }

            if (_inputPtr >= _inputEnd) {
                if (!_loadMore()) {
                    _reportInvalidEOF(": was expecting closing quote for a string value",
                            JsonToken.VALUE_STRING);
                }
            }
            char c = _inputBuffer[_inputPtr++];
            int i = c;
            if (i < maxCode && codes[i] != 0) {
                if (i == INT_QUOTE) {
                    break;
                } else if (i == INT_BACKSLASH) {
                    /* Although chars outside of BMP are to be escaped as
                     * an UTF-16 surrogate pair, does that affect decoding?
                     * For now let's assume it does not.
                     */
                    c = _decodeEscaped();
                } else if (i < INT_SPACE) {
                    _throwUnquotedSpace(i, "string value");
                } // anything else?
            }

            // Ok, let's add char to output:
            outBuf[outPtr++] = c;
        }
        _textBuffer.setCurrentLength(outPtr);
    }
}

I debugged it for a big string and it loads it in segments, in a rather unexpected way. There has to be a bug somewhere but it's a good scaffolding to begin with. Also there is some problem with the read string length, it reads about 100kb instead of 350kb, maybe it has something to do with MAX_SEGMENT_LEN but I don't think so. I guess it's an encoding issue. And then you use it like that, write your own factory method

var factory = new ChunkJsonFactory();
try (var parser = factory.createParser(new FileReader("src/main/resources/sample-json.json"))){
        while(parser.nextToken()!=null){
        if(parser.getCurrentToken().isScalarValue()){
            parser.getText(writer);
        }
    }
}

Inheriting Writer

For example, if you want to count the characters inside of the string, you can extend Writer and use the following code

public class JsonWriter extends Writer {

    @Getter
    @Setter
    private int count;

    @Setter
    private char letterToCount = 'a';

    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        for (int i = off; i < off + len; i++) {
            if (cbuf[i] == letterToCount) {
                count++;
            }
        }
    }
}

I created a sample repository here

🌐
Oracle
docs.oracle.com › javaee › 7 › api › javax › json › stream › package-summary.html
javax.json.stream (Java(TM) EE 7 Specification APIs)
The streaming API consists of the interfaces JsonParser and JsonGenerator. The interface JsonParser contains methods to parse JSON in a streaming way.
Top answer
1 of 6
40

You are passing into the JSONObject constructor an instance of a StringBuilder class.

This is using the JSONObject(Object) constructor, not the JSONObject(String) one.

Your code should be:

JSONObject jsonObj = new JSONObject(jsonString.toString());
2 of 6
5

@Nishit, JSONObject does not natively understand how to parse through a StringBuilder; instead you appear to be using the JSONObject(java.lang.Object bean) constructor to create the JSONObject, however passing it a StringBuilder.

See this link for more information on that particular constructor.

http://www.json.org/javadoc/org/json/JSONObject.html#JSONObject%28java.lang.Object%29

When a constructor calls for a java.lang.Object class, more than likely it's really telling you that you're expected to create your own class (since all Classes ultimately extend java.lang.Object) and that it will interface with that class in a specific way, albeit normally it will call for an interface instead (hence the name) OR it can accept any class and interface with it "abstractly" such as calling .toString() on it. Bottom line, you typically can't just pass it any class and expect it to work.

At any rate, this particular constructor is explained as such:

Construct a JSONObject from an Object using bean getters. It reflects on all of the public methods of the object. For each of the methods with no parameters and a name starting with "get" or "is" followed by an uppercase letter, the method is invoked, and a key and the value returned from the getter method are put into the new JSONObject. The key is formed by removing the "get" or "is" prefix. If the second remaining character is not upper case, then the first character is converted to lower case. For example, if an object has a method named "getName", and if the result of calling object.getName() is "Larry Fine", then the JSONObject will contain "name": "Larry Fine".

So, what this means is that it's expecting you to create your own class that implements get or is methods (i.e.

public String getName() {...}

or

public boolean isValid() {...}

So, to solve your problem, if you really want that higher level of control and want to do some manipulation (e.g. modify some values, etc.) but still use StringBuilder to dynamically generate the code, you can create a class that extends the StringBuilder class so that you can use the append feature, but implement get/is methods to allow JSONObject to pull the data out of it, however this is likely not what you want/need and depending on the JSON, you might spend a lot of time and energy creating the private fields and get/is methods (or use an IDE to do it for you) or it might be all for naught if you don't necessarily know the breakdown of the JSON string.

So, you can very simply call toString() on the StringBuilder which will provide a String representation of the StringBuilder instance and passing that to the JSONObject constructor, such as below:

...
StringBuilder jsonString = new StringBuilder();
while((readAPIResponse = br.readLine()) != null){
    jsonString.append(readAPIResponse);
}
JSONObject jsonObj = new JSONObject(jsonString.toString());
...
🌐
GitHub
gist.github.com › KengoTODA › 1159286
InputStream from JSONObject. This class will help feeding JSON from the application server. · GitHub
InputStream from JSONObject. This class will help feeding JSON from the application server. - JSONInputStream.diff
🌐
Ibmstreams
ibmstreams.github.io › streamsx.topology › doc › javadoc › com › ibm › streamsx › topology › json › JSONStreams.html
JSONStreams (Java Application API for IBM Streams)
public static TStream<com.ibm.json.java.JSONObject> deserialize(TStream<java.lang.String> stream) Declare a stream of JSON objects from a stream of serialized JSON tuples. If the serialized JSON is a simple value or an array, then a JSON object is created, with a single attribute payload containing ...
🌐
GitHub
github.com › wanglingsong › JsonSurfer
GitHub - wanglingsong/JsonSurfer: A streaming JsonPath processor in Java · GitHub
Iterator iterator = surfer.iterator(read("sample.json"), JsonPathCompiler.compile("$.store.book[*]")); Java8 user can also convert the iterator into a Stream · Stream<Object> targetStream = StreamSupport.stream( Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false); Functions implmented by Streams API · public static Stream<Object> toStream(String json, String path) { Iterator<Object> iterator = JsonSurferJackson.INSTANCE.iterator(json, JsonPathCompiler.compile(path)); return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false);
Starred by 315 users
Forked by 58 users
Languages: Java 99.5% | ANTLR 0.5%