From 2c720b26abcd8ef757291f2baf1311a4aab58121 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Fri, 5 Feb 2016 19:24:42 +0100
Subject: [PATCH] added a constructor from an input stream

---
 doc/examples/basic_json__istream.cpp    | 55 +++++++++++++++++++++++++
 doc/examples/basic_json__istream.link   |  1 +
 doc/examples/basic_json__istream.output | 34 +++++++++++++++
 src/json.hpp                            | 25 +++++++++++
 src/json.hpp.re2c                       | 25 +++++++++++
 test/unit.cpp                           | 36 ++++++++++++++++
 6 files changed, 176 insertions(+)
 create mode 100644 doc/examples/basic_json__istream.cpp
 create mode 100644 doc/examples/basic_json__istream.link
 create mode 100644 doc/examples/basic_json__istream.output

diff --git a/doc/examples/basic_json__istream.cpp b/doc/examples/basic_json__istream.cpp
new file mode 100644
index 00000000..71f16ed3
--- /dev/null
+++ b/doc/examples/basic_json__istream.cpp
@@ -0,0 +1,55 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // a JSON text
+    auto text = R"(
+    {
+        "Image": {
+            "Width":  800,
+            "Height": 600,
+            "Title":  "View from 15th Floor",
+            "Thumbnail": {
+                "Url":    "http://www.example.com/image/481989943",
+                "Height": 125,
+                "Width":  100
+            },
+            "Animated" : false,
+            "IDs": [116, 943, 234, 38793]
+        }
+    }
+    )";
+
+    // fill a stream with JSON text
+    std::stringstream ss;
+    ss << text;
+
+    // create JSON from stream
+    json j_complete(ss);
+    std::cout << std::setw(4) << j_complete << "\n\n";
+
+
+    // define parser callback
+    json::parser_callback_t cb = [](int depth, json::parse_event_t event, json & parsed)
+    {
+        // skip object elements with key "Thumbnail"
+        if (event == json::parse_event_t::key and parsed == json("Thumbnail"))
+        {
+            return false;
+        }
+        else
+        {
+            return true;
+        }
+    };
+
+    // fill a stream with JSON text
+    ss.clear();
+    ss << text;
+
+    // create JSON from stream (with callback)
+    json j_filtered(ss, cb);
+    std::cout << std::setw(4) << j_filtered << '\n';
+}
\ No newline at end of file
diff --git a/doc/examples/basic_json__istream.link b/doc/examples/basic_json__istream.link
new file mode 100644
index 00000000..e5475436
--- /dev/null
+++ b/doc/examples/basic_json__istream.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/lUltfC5TlmxeUq5X"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__istream.output b/doc/examples/basic_json__istream.output
new file mode 100644
index 00000000..279a7ff7
--- /dev/null
+++ b/doc/examples/basic_json__istream.output
@@ -0,0 +1,34 @@
+{
+    "Image": {
+        "Animated": false,
+        "Height": 600,
+        "IDs": [
+            116,
+            943,
+            234,
+            38793
+        ],
+        "Thumbnail": {
+            "Height": 125,
+            "Url": "http://www.example.com/image/481989943",
+            "Width": 100
+        },
+        "Title": "View from 15th Floor",
+        "Width": 800
+    }
+}
+
+{
+    "Image": {
+        "Animated": false,
+        "Height": 600,
+        "IDs": [
+            116,
+            943,
+            234,
+            38793
+        ],
+        "Title": "View from 15th Floor",
+        "Width": 800
+    }
+}
diff --git a/src/json.hpp b/src/json.hpp
index 39ef1fe8..effdeb13 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1806,6 +1806,31 @@ class basic_json
         }
     }
 
+    /*!
+    @brief construct a JSON value given an input stream
+
+    @param[in,out] i  stream to read a serialized JSON value from
+    @param[in] cb a parser callback function of type @ref parser_callback_t
+    which is used to control the deserialization by filtering unwanted values
+    (optional)
+
+    @complexity Linear in the length of the input. The parser is a predictive
+    LL(1) parser. The complexity can be higher if the parser callback function
+    @a cb has a super-linear complexity.
+
+    @note A UTF-8 byte order mark is silently ignored.
+
+    @liveexample{The example below demonstrates constructing a JSON value from
+    a `std::stringstream` with and without callback
+    function.,basic_json__istream}
+
+    @since version 2.0.0
+    */
+    basic_json(std::istream& i, parser_callback_t cb = nullptr)
+    {
+        *this = parser(i, cb).parse();
+    }
+
     ///////////////////////////////////////
     // other constructors and destructor //
     ///////////////////////////////////////
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index f4909629..e0636ab3 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1806,6 +1806,31 @@ class basic_json
         }
     }
 
+    /*!
+    @brief construct a JSON value given an input stream
+
+    @param[in,out] i  stream to read a serialized JSON value from
+    @param[in] cb a parser callback function of type @ref parser_callback_t
+    which is used to control the deserialization by filtering unwanted values
+    (optional)
+
+    @complexity Linear in the length of the input. The parser is a predictive
+    LL(1) parser. The complexity can be higher if the parser callback function
+    @a cb has a super-linear complexity.
+
+    @note A UTF-8 byte order mark is silently ignored.
+
+    @liveexample{The example below demonstrates constructing a JSON value from
+    a `std::stringstream` with and without callback
+    function.,basic_json__istream}
+
+    @since version 2.0.0
+    */
+    basic_json(std::istream& i, parser_callback_t cb = nullptr)
+    {
+        *this = parser(i, cb).parse();
+    }
+
     ///////////////////////////////////////
     // other constructors and destructor //
     ///////////////////////////////////////
diff --git a/test/unit.cpp b/test/unit.cpp
index 1c3957a4..fc77fe28 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -1264,6 +1264,42 @@ TEST_CASE("constructors")
             }
         }
     }
+
+    SECTION("create a JSON value from an input stream")
+    {
+        SECTION("sts::stringstream")
+        {
+            std::stringstream ss;
+            ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
+            json j(ss);
+            CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
+        }
+
+        SECTION("with callback function")
+        {
+            std::stringstream ss;
+            ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
+            json j(ss, [](int, json::parse_event_t, const json & val)
+            {
+                // filter all number(2) elements
+                if (val == json(2))
+                {
+                    return false;
+                }
+                else
+                {
+                    return true;
+                }
+            });
+            CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}}));
+        }
+
+        SECTION("std::ifstream")
+        {
+            std::ifstream f("test/json_tests/pass1.json");
+            json j(f);
+        }
+    }
 }
 
 TEST_CASE("other constructors and destructor")