Add option to ignore CBOR tags
This issue implements a proposal to address #1968. CBOR tags can be ignored then. To avoid breaking changes, the default will still throw a parse error. A treatment of CBOR tags is also the prerequisite of #2244.

Closes #1968.
{"label": "nlohmann:develop", "ref": "develop", "sha": "1a521cbd3657eea6ba90cded2aecca93e3cd78d4"}
Using 456478b3c50d60100dbb1fb9bc931f370a2c1c28 (3.7.3)

The following code should parse a CBOR value:

```
#include "json.hpp"

#include <sstream>

class NullSAXParser : public nlohmann::json_sax<nlohmann::json> {
public:
  bool null() override {
  	return true;
  }

  bool boolean(bool val) override {
    return true;
   }

  bool number_integer(number_integer_t val) override {
    return true;
  }

  bool number_unsigned(number_unsigned_t val) override {
    return true;
  }

  bool number_float(number_float_t val, const string_t &s) override {
    return true;
  }

  bool string(string_t &val) override {
    return true;
  }

  bool start_object(std::size_t elements) override {
    return true;
  }

  bool key(string_t &val) override {
    return true;
  }

  bool end_object() override {
    return true;
  }

  bool start_array(std::size_t elements) override {
    return true;
  }

  bool end_array() override {
    return true;
  }

  bool parse_error(std::size_t position, const std::string &last_token,
                   const nlohmann::detail::exception &ex) override {
    throw ex;
  }
};

int main() {
    std::stringstream in("\xd9\xd9\xf7");
    NullSAXParser parser;
	nlohmann::json::sax_parse(in, &parser, nlohmann::json::input_format_t::cbor);	
}
```

The value I'm parsing (`0xd9d9f7`) is simply the optimal "magic number" tag for CBOR documents. From [the specification](https://tools.ietf.org/html/rfc7049#section-2.4.5):

>    The serialization of this tag is 0xd9d9f7, which appears not to be in
>    use as a distinguishing mark for frequently used file types.  In
>    particular, it is not a valid start of a Unicode text in any Unicode
>    encoding if followed by a valid CBOR data item.

Byte 0xd9 should be fine because it is equal to `(6 << 5) | 25`, in other words it has a major type of 6 (a tag), and lower 5 bits of 25, which for a tag means the actual tag value follows in a `uint16`, so it should just skip the following 2 bytes.

Some extra code needs to be added [here](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/binary_reader.hpp#L610). It doesn't understand tags at all.
CBOR parser doesn't skip tags
diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp
index d2f75cfde4..a650b2d01f 100644
--- a/include/nlohmann/detail/input/binary_reader.hpp
+++ b/include/nlohmann/detail/input/binary_reader.hpp
@@ -24,6 +24,13 @@ namespace nlohmann
 namespace detail
 {
 
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+    error,  ///< throw a parse_error exception in case of a tag
+    ignore   ///< ignore tags
+};
+
 /*!
 @brief determine system byte order
 
@@ -78,13 +85,15 @@ class binary_reader
     @param[in] format  the binary format to parse
     @param[in] sax_    a SAX event processor
     @param[in] strict  whether to expect the input to be consumed completed
+    @param[in] tag_handler  how to treat CBOR tags
 
     @return
     */
     JSON_HEDLEY_NON_NULL(3)
     bool sax_parse(const input_format_t format,
                    json_sax_t* sax_,
-                   const bool strict = true)
+                   const bool strict = true,
+                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         sax = sax_;
         bool result = false;
@@ -96,7 +105,7 @@ class binary_reader
                 break;
 
             case input_format_t::cbor:
-                result = parse_cbor_internal();
+                result = parse_cbor_internal(true, tag_handler);
                 break;
 
             case input_format_t::msgpack:
@@ -386,10 +395,12 @@ class binary_reader
     @param[in] get_char  whether a new character should be retrieved from the
                          input (true, default) or whether the last read
                          character should be considered instead
+    @param[in] tag_handler how CBOR tags should be treated
 
     @return whether a valid CBOR value was passed to the SAX parser
     */
-    bool parse_cbor_internal(const bool get_char = true)
+    bool parse_cbor_internal(const bool get_char = true,
+                             cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         switch (get_char ? get() : current)
         {
@@ -678,6 +689,73 @@ class binary_reader
             case 0xBF: // map (indefinite length)
                 return get_cbor_object(std::size_t(-1));
 
+            case 0xC6: // tagged item
+            case 0xC7:
+            case 0xC8:
+            case 0xC9:
+            case 0xCA:
+            case 0xCB:
+            case 0xCC:
+            case 0xCD:
+            case 0xCE:
+            case 0xCF:
+            case 0xD0:
+            case 0xD1:
+            case 0xD2:
+            case 0xD3:
+            case 0xD4:
+            case 0xD8: // tagged item (1 bytes follow)
+            case 0xD9: // tagged item (2 bytes follow)
+            case 0xDA: // tagged item (4 bytes follow)
+            case 0xDB: // tagged item (8 bytes follow)
+            {
+                switch (tag_handler)
+                {
+                    case cbor_tag_handler_t::error:
+                    {
+                        auto last_token = get_token_string();
+                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
+                    }
+
+                    case cbor_tag_handler_t::ignore:
+                    {
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            default:
+                                break;
+                        }
+                        return parse_cbor_internal(true, tag_handler);
+                    }
+
+                    default:            // LCOV_EXCL_LINE
+                        JSON_ASSERT(false);  // LCOV_EXCL_LINE
+                }
+            }
+
             case 0xF4: // false
                 return sax->boolean(false);
 
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index 7c2cf2b161..1da9c3858c 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -229,6 +229,8 @@ class basic_json
     using json_serializer = JSONSerializer<T, SFINAE>;
     /// how to treat decoding errors
     using error_handler_t = detail::error_handler_t;
+    /// how to treat CBOR tags
+    using cbor_tag_handler_t = detail::cbor_tag_handler_t;
     /// helper type for initializer lists of basic_json values
     using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
 
@@ -7048,7 +7050,7 @@ class basic_json
     vector in CBOR format.,to_cbor}
 
     @sa http://cbor.io
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         analogous deserialization
     @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
     @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
@@ -7436,6 +7438,7 @@ class basic_json
                        (true by default)
     @param[in] allow_exceptions  whether to throw exceptions in case of a
     parse error (optional, true by default)
+    @param[in] tag_handler how to treat CBOR tags (optional, error by default)
 
     @return deserialized JSON value; in case of a parse error and
             @a allow_exceptions set to `false`, the return value will be
@@ -7462,34 +7465,36 @@ class basic_json
     @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
            consume input adapters, removed start_index parameter, and added
            @a strict parameter since 3.0.0; added @a allow_exceptions parameter
-           since 3.2.0
+           since 3.2.0; added @a tag_handler parameter since 3.9.0.
     */
     template<typename InputType>
     JSON_HEDLEY_WARN_UNUSED_RESULT
     static basic_json from_cbor(InputType&& i,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = detail::input_adapter(std::forward<InputType>(i));
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
     /*!
-    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)
+    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t)
     */
     template<typename IteratorType>
     JSON_HEDLEY_WARN_UNUSED_RESULT
     static basic_json from_cbor(IteratorType first, IteratorType last,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = detail::input_adapter(std::move(first), std::move(last));
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
@@ -7498,9 +7503,10 @@ class basic_json
     JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
     static basic_json from_cbor(const T* ptr, std::size_t len,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
-        return from_cbor(ptr, ptr + len, strict, allow_exceptions);
+        return from_cbor(ptr, ptr + len, strict, tag_handler);
     }
 
 
@@ -7508,12 +7514,13 @@ class basic_json
     JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
     static basic_json from_cbor(detail::span_input_adapter&& i,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = i.get();
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
@@ -7591,7 +7598,7 @@ class basic_json
 
     @sa http://msgpack.org
     @sa @ref to_msgpack(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for
         the related UBJSON format
@@ -7709,7 +7716,7 @@ class basic_json
     @sa http://ubjson.org
     @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
              analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
         the related MessagePack format
@@ -7824,7 +7831,7 @@ class basic_json
 
     @sa http://bsonspec.org/spec.html
     @sa @ref to_bson(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
         the related MessagePack format
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index 82c0d48477..b0d4d72a2a 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -5802,6 +5802,13 @@ namespace nlohmann
 namespace detail
 {
 
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+    error,  ///< throw a parse_error exception in case of a tag
+    ignore   ///< ignore tags
+};
+
 /*!
 @brief determine system byte order
 
@@ -5856,13 +5863,15 @@ class binary_reader
     @param[in] format  the binary format to parse
     @param[in] sax_    a SAX event processor
     @param[in] strict  whether to expect the input to be consumed completed
+    @param[in] tag_handler  how to treat CBOR tags
 
     @return
     */
     JSON_HEDLEY_NON_NULL(3)
     bool sax_parse(const input_format_t format,
                    json_sax_t* sax_,
-                   const bool strict = true)
+                   const bool strict = true,
+                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         sax = sax_;
         bool result = false;
@@ -5874,7 +5883,7 @@ class binary_reader
                 break;
 
             case input_format_t::cbor:
-                result = parse_cbor_internal();
+                result = parse_cbor_internal(true, tag_handler);
                 break;
 
             case input_format_t::msgpack:
@@ -6164,10 +6173,12 @@ class binary_reader
     @param[in] get_char  whether a new character should be retrieved from the
                          input (true, default) or whether the last read
                          character should be considered instead
+    @param[in] tag_handler how CBOR tags should be treated
 
     @return whether a valid CBOR value was passed to the SAX parser
     */
-    bool parse_cbor_internal(const bool get_char = true)
+    bool parse_cbor_internal(const bool get_char = true,
+                             cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         switch (get_char ? get() : current)
         {
@@ -6456,6 +6467,73 @@ class binary_reader
             case 0xBF: // map (indefinite length)
                 return get_cbor_object(std::size_t(-1));
 
+            case 0xC6: // tagged item
+            case 0xC7:
+            case 0xC8:
+            case 0xC9:
+            case 0xCA:
+            case 0xCB:
+            case 0xCC:
+            case 0xCD:
+            case 0xCE:
+            case 0xCF:
+            case 0xD0:
+            case 0xD1:
+            case 0xD2:
+            case 0xD3:
+            case 0xD4:
+            case 0xD8: // tagged item (1 bytes follow)
+            case 0xD9: // tagged item (2 bytes follow)
+            case 0xDA: // tagged item (4 bytes follow)
+            case 0xDB: // tagged item (8 bytes follow)
+            {
+                switch (tag_handler)
+                {
+                    case cbor_tag_handler_t::error:
+                    {
+                        auto last_token = get_token_string();
+                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
+                    }
+
+                    case cbor_tag_handler_t::ignore:
+                    {
+                        switch (current)
+                        {
+                            case 0xD8:
+                            {
+                                std::uint8_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xD9:
+                            {
+                                std::uint16_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xDA:
+                            {
+                                std::uint32_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            case 0xDB:
+                            {
+                                std::uint64_t len{};
+                                get_number(input_format_t::cbor, len);
+                                break;
+                            }
+                            default:
+                                break;
+                        }
+                        return parse_cbor_internal(true, tag_handler);
+                    }
+
+                    default:            // LCOV_EXCL_LINE
+                        JSON_ASSERT(false);  // LCOV_EXCL_LINE
+                }
+            }
+
             case 0xF4: // false
                 return sax->boolean(false);
 
@@ -16217,6 +16295,8 @@ class basic_json
     using json_serializer = JSONSerializer<T, SFINAE>;
     /// how to treat decoding errors
     using error_handler_t = detail::error_handler_t;
+    /// how to treat CBOR tags
+    using cbor_tag_handler_t = detail::cbor_tag_handler_t;
     /// helper type for initializer lists of basic_json values
     using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
 
@@ -23036,7 +23116,7 @@ class basic_json
     vector in CBOR format.,to_cbor}
 
     @sa http://cbor.io
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         analogous deserialization
     @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
     @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
@@ -23424,6 +23504,7 @@ class basic_json
                        (true by default)
     @param[in] allow_exceptions  whether to throw exceptions in case of a
     parse error (optional, true by default)
+    @param[in] tag_handler how to treat CBOR tags (optional, error by default)
 
     @return deserialized JSON value; in case of a parse error and
             @a allow_exceptions set to `false`, the return value will be
@@ -23450,34 +23531,36 @@ class basic_json
     @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
            consume input adapters, removed start_index parameter, and added
            @a strict parameter since 3.0.0; added @a allow_exceptions parameter
-           since 3.2.0
+           since 3.2.0; added @a tag_handler parameter since 3.9.0.
     */
     template<typename InputType>
     JSON_HEDLEY_WARN_UNUSED_RESULT
     static basic_json from_cbor(InputType&& i,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = detail::input_adapter(std::forward<InputType>(i));
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
     /*!
-    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)
+    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t)
     */
     template<typename IteratorType>
     JSON_HEDLEY_WARN_UNUSED_RESULT
     static basic_json from_cbor(IteratorType first, IteratorType last,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = detail::input_adapter(std::move(first), std::move(last));
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
@@ -23486,9 +23569,10 @@ class basic_json
     JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
     static basic_json from_cbor(const T* ptr, std::size_t len,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
-        return from_cbor(ptr, ptr + len, strict, allow_exceptions);
+        return from_cbor(ptr, ptr + len, strict, tag_handler);
     }
 
 
@@ -23496,12 +23580,13 @@ class basic_json
     JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
     static basic_json from_cbor(detail::span_input_adapter&& i,
                                 const bool strict = true,
-                                const bool allow_exceptions = true)
+                                const bool allow_exceptions = true,
+                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
     {
         basic_json result;
         detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
         auto ia = i.get();
-        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict);
+        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
         return res ? result : basic_json(value_t::discarded);
     }
 
@@ -23579,7 +23664,7 @@ class basic_json
 
     @sa http://msgpack.org
     @sa @ref to_msgpack(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for
         the related UBJSON format
@@ -23697,7 +23782,7 @@ class basic_json
     @sa http://ubjson.org
     @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
              analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
         the related MessagePack format
@@ -23812,7 +23897,7 @@ class basic_json
 
     @sa http://bsonspec.org/spec.html
     @sa @ref to_bson(const basic_json&) for the analogous serialization
-    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the
+    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the
         related CBOR format
     @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for
         the related MessagePack format

diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp
index 34b57043f9..6af06bf60f 100644
--- a/test/src/unit-cbor.cpp
+++ b/test/src/unit-cbor.cpp
@@ -2548,3 +2548,176 @@ TEST_CASE("examples from RFC 7049 Appendix A")
         CHECK(json::parse("{\"Fun\": true, \"Amt\": -2}") == json::from_cbor(std::vector<uint8_t>({0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff})));
     }
 }
+
+TEST_CASE("Tagged values")
+{
+    json j = "s";
+    auto v = json::to_cbor(j);
+
+    SECTION("0xC6..0xD4")
+    {
+        for (std::uint8_t b :
+                {
+                    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4
+                })
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), b);
+
+            // check that parsing fails in error mode
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+
+            // check that parsing succeeds and gets original value in ignore mode
+            auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore);
+            CHECK(j_tagged == j);
+        }
+    }
+
+    SECTION("0xD8 - 1 byte follows")
+    {
+        SECTION("success")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xD8); // tag
+
+            // check that parsing fails in error mode
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+
+            // check that parsing succeeds and gets original value in ignore mode
+            auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore);
+            CHECK(j_tagged == j);
+        }
+
+        SECTION("missing byte after tag")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0xD8); // tag
+
+            // check that parsing fails in all modes
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error);
+        }
+    }
+
+    SECTION("0xD9 - 2 byte follow")
+    {
+        SECTION("success")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xD9); // tag
+
+            // check that parsing fails in error mode
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+
+            // check that parsing succeeds and gets original value in ignore mode
+            auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore);
+            CHECK(j_tagged == j);
+        }
+
+        SECTION("missing byte after tag")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xD9); // tag
+
+            // check that parsing fails in all modes
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error);
+        }
+    }
+
+    SECTION("0xDA - 4 bytes follow")
+    {
+        SECTION("success")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xDA); // tag
+
+            // check that parsing fails in error mode
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+
+            // check that parsing succeeds and gets original value in ignore mode
+            auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore);
+            CHECK(j_tagged == j);
+        }
+
+        SECTION("missing bytes after tag")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xDA); // tag
+
+            // check that parsing fails in all modes
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error);
+        }
+    }
+
+    SECTION("0xDB - 8 bytes follow")
+    {
+        SECTION("success")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xDB); // tag
+
+            // check that parsing fails in error mode
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+
+            // check that parsing succeeds and gets original value in ignore mode
+            auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore);
+            CHECK(j_tagged == j);
+        }
+
+        SECTION("missing byte after tag")
+        {
+            // add tag to value
+            auto v_tagged = v;
+            v_tagged.insert(v_tagged.begin(), 0x42); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x23); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x22); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0x11); // 1 byte
+            v_tagged.insert(v_tagged.begin(), 0xDB); // tag
+
+            // check that parsing fails in all modes
+            CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error);
+            CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error);
+        }
+    }
+}

{"test-noexcept": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-large_json": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-testsuites": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-convenience": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_fetch_content_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-reference_access": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-wstring": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-to_chars": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "download_test_data": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-ubjson": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-bson": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-allocator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-ordered_json": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-element_access2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_minver_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-cbor": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-regression": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_minver_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-user_defined_input": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-capacity": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_lexer": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-udt": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-concepts": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-comparison": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-modifiers": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_add_subdirectory_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-unicode": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_iterator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-constructor2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-readme": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-iterators1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-conversions": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-msgpack": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-iterators2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-inspection": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-meta": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-items": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-constructor1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_parser": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-json_patch": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_fetch_content_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-element_access1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-merge_patch": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-serialization": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-json_pointer": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-deserialization": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-assert_macro": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-pointer_access": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-alt-string": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-algorithms": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-udt_macro": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_const_iterator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_add_subdirectory_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"test-noexcept": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-large_json": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-testsuites": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-convenience": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_fetch_content_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-reference_access": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-wstring": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-to_chars": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "download_test_data": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-ubjson": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-bson": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-allocator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-ordered_json": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-element_access2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_minver_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-cbor": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-regression": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_minver_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-user_defined_input": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-capacity": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_lexer": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-udt": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-concepts": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-comparison": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-modifiers": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_add_subdirectory_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-unicode": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_iterator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-constructor2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-readme": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-iterators1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-conversions": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-msgpack": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-iterators2": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-inspection": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-meta": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-items": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-constructor1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_parser": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-json_patch": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_fetch_content_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-element_access1": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-merge_patch": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-serialization": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-json_pointer": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-deserialization": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-assert_macro": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-pointer_access": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-alt-string": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_import_configure": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-algorithms": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-udt_macro": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "test-class_const_iterator": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "cmake_add_subdirectory_build": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"passed_count": 55, "failed_count": 0, "skipped_count": 0, "passed_tests": ["test-noexcept", "test-udt_macro", "test-large_json", "test-testsuites", "test-convenience", "cmake_fetch_content_build", "test-reference_access", "test-wstring", "cmake_import_build", "test-to_chars", "download_test_data", "test-ubjson", "test-bson", "test-allocator", "test-ordered_json", "test-element_access2", "cmake_import_minver_build", "test-cbor", "test-regression", "cmake_import_minver_configure", "test-user_defined_input", "test-capacity", "test-class_lexer", "test-udt", "test-concepts", "test-comparison", "test-class_const_iterator", "test-modifiers", "cmake_add_subdirectory_configure", "test-unicode", "test-class_iterator", "test-constructor2", "test-readme", "test-iterators1", "test-conversions", "test-msgpack", "test-iterators2", "test-inspection", "test-meta", "test-items", "test-constructor1", "test-json_patch", "cmake_fetch_content_configure", "test-element_access1", "test-merge_patch", "test-serialization", "test-json_pointer", "test-assert_macro", "test-pointer_access", "test-alt-string", "cmake_import_configure", "test-algorithms", "test-class_parser", "test-deserialization", "cmake_add_subdirectory_build"], "failed_tests": [], "skipped_tests": []}
{"passed_count": 0, "failed_count": 0, "skipped_count": 0, "passed_tests": [], "failed_tests": [], "skipped_tests": []}
{"passed_count": 55, "failed_count": 0, "skipped_count": 0, "passed_tests": ["test-noexcept", "test-udt_macro", "test-large_json", "test-testsuites", "test-convenience", "cmake_fetch_content_build", "test-reference_access", "test-wstring", "cmake_import_build", "test-to_chars", "download_test_data", "test-ubjson", "test-bson", "test-allocator", "test-ordered_json", "test-element_access2", "cmake_import_minver_build", "test-cbor", "test-regression", "cmake_import_minver_configure", "test-user_defined_input", "test-capacity", "test-class_lexer", "test-udt", "test-concepts", "test-comparison", "test-class_const_iterator", "test-modifiers", "cmake_add_subdirectory_configure", "test-unicode", "test-class_iterator", "test-constructor2", "test-readme", "test-iterators1", "test-conversions", "test-msgpack", "test-iterators2", "test-inspection", "test-meta", "test-items", "test-constructor1", "test-json_patch", "cmake_fetch_content_configure", "test-element_access1", "test-merge_patch", "test-serialization", "test-json_pointer", "test-assert_macro", "test-pointer_access", "test-alt-string", "cmake_import_configure", "test-algorithms", "test-class_parser", "test-deserialization", "cmake_add_subdirectory_build"], "failed_tests": [], "skipped_tests": []}