【问题标题】:How to parse a JSON Input stream如何解析 JSON 输入流
【发布时间】:2011-09-24 14:36:36
【问题描述】:

我正在使用java调用返回JSON对象的url:

url = new URL("my URl");
urlInputStream = url.openConnection().getInputStream();

如何将响应转换为字符串形式并进行解析?

【问题讨论】:

  • 欢迎堆栈溢出!请记住在发布问题时正确格式化您的代码。

标签: java android json


【解决方案1】:

使用jackson将json输入流转换为地图或对象http://jackson.codehaus.org/

还有一些其他有用的json库,你可以google:json java

【讨论】:

  • 请通过每个库的示例改进此答案
  • 这基本上说明不了任何问题。
【解决方案2】:

使用库。

  • GSON
  • Jackson
  • 或现有的许多其他 JSON 库之一。

【讨论】:

    【解决方案3】:
    {
        InputStream is = HTTPClient.get(url);
        InputStreamReader reader = new InputStreamReader(is);
        JSONTokener tokenizer = new JSONTokener(reader);
        JSONObject jsonObject = new JSONObject(tokenizer);
    }
    

    【讨论】:

    • JSONTokener 不需要 JSON 字符串,而不是 InputStreamReader 吗?
    • link 表示对于Android,JSONTokener只接受一个String。
    【解决方案4】:

    我建议你必须使用 Reader 来转换你的 InputStream。

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

    我尝试了 in.toString() 但它返回:

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

    (就像文档说它从 Object 派生到 toString)

    【讨论】:

    • 如果性能是一个问题,请注意 StringBuilder 的默认容量为 16 个字符,您可能希望通过使用大于平均文本长度的长度来增加它,例如 new StringBuilder(2048)。无论如何,逐行阅读可能并不是最好的性能。
    • 如果您的 json 大约 50 KB 或更多并且包含非英文字母,这是非常不安全的。所以最好不要使用 Strings 并使用 InputStreams(GSON 可以使用它)
    【解决方案5】:

    对于那些指出您不能像这样使用 InputStream 的 toString 方法的人,请参阅https://stackoverflow.com/a/5445161/1304830

    我的正确答案是:

    import org.json.JSONObject;
    
    public static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }
    
    ...
    
    JSONObject json = new JSONObject(convertStreamToString(url.openStream());
    

    【讨论】:

    • 将流转换为字符串需要您将整个内容保存在内存中,而流则不会。
    • 尽管它可能会占用大量内存,但这种方法确实有一些优点,因为对于小型 JSON 对象,它非常清晰和紧凑
    • 如果您的 json 大约 50 KB 或更多并且包含非英文字母,这是非常不安全的。所以最好不要使用Strings而使用InputStreams(GSON可以使用它)
    【解决方案6】:

    所有当前的答案都假设可以将整个 JSON 拉入内存,其中 InputStream 的优点是您可以一点一点地读取输入。如果您想避免一次读取整个 Json 文件,那么我建议您使用 Jackson 库(这是我个人最喜欢的,但我相信像 Gson 这样的其他人也有类似的功能)。

    使用 Jackson,您可以使用 JsonParser 一次读取一个部分。下面是我编写的代码示例,它将 JsonObjects 数组的读取包装在迭代器中。如果您只是想看一个 Jackson 的示例,请查看 initJsonParser、initFirstElement 和 initNextObject 方法。

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

    如果您不关心内存使用情况,那么读取整个文件并将其解析为其他答案中提到的一个大 Json 肯定会更容易。

    【讨论】:

    • 这是一个非常有效的实现,它允许我用 only -Xmx16M 解析一个 15MB JSON 文件,而我遇到了带有实现的 -Xmx128M 的 OOM 异常将整个 JSON 放入内存中。
    【解决方案7】:

    如果你喜欢使用Jackson DatabindSpring 默认使用它的HttpMessageConverters),那么你可以使用ObjectMapper.readTree(InputStream) API。例如,

    ObjectMapper mapper = new ObjectMapper();
    JsonNode json = mapper.readTree(myInputStream);
    

    【讨论】:

      【解决方案8】:

      如果您有 JSON 文件,您可以将其设置在 assets 文件夹中,然后使用此代码调用它

      InputStream in = mResources.getAssets().open("fragrances.json"); 
      // where mResources object from Resources class
      

      【讨论】:

      • 你没有解析,只是流对象,不是json对象
      【解决方案9】:

      这个例子从一个对象流中读取所有对象, 假设您需要 CustomObjects 而不是 Map:

              ObjectMapper mapper = new ObjectMapper();
              JsonParser parser = mapper.getFactory().createParser( source );
              if(parser.nextToken() != JsonToken.START_ARRAY) {
                throw new IllegalStateException("Expected an array");
              }
              while(parser.nextToken() == JsonToken.START_OBJECT) {
                // read everything from this START_OBJECT to the matching END_OBJECT
                // and return it as a tree model ObjectNode
                ObjectNode node = mapper.readTree(parser);
                CustomObject custom = mapper.convertValue( node, CustomObject.class );
                 // do whatever you need to do with this object
                System.out.println( "" + custom );
              }
              parser.close();
      

      这个答案是用Use Jackson To Stream Parse an Array of Json ObjectsConvert JsonNode into Object组成的

      【讨论】:

        【解决方案10】:

        带有 Gson 的 Kotlin 版本

        读取响应 JSON:

        val response = BufferedReader(
                           InputStreamReader(conn.inputStream, "UTF-8")
                       ).use { it.readText() }
        

        我们可以使用 Gson 来解析响应:

        val model = Gson().fromJson(response, YourModelClass::class.java)
        

        【讨论】:

          【解决方案11】:

          我建议使用javax.json.Json factory 作为简洁可能的解决方案:

          JsonObject json = Json.createReader(yourInputStream).readObject();
          

          享受吧!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-11
            • 1970-01-01
            • 2023-01-11
            • 1970-01-01
            相关资源
            最近更新 更多