/* file JsonPrinter.cpp * * author: Roland Conybeare, Aug 2022 */ #include "PrintJson.hpp" //#include "time/Time.hpp" #include "xo/reflect/TypeDescr.hpp" #include "xo/indentlog/print/tag.hpp" #include namespace xo { using xo::time::utc_nanos; using xo::reflect::Metatype; using xo::reflect::Reflect; using xo::reflect::SelfTagging; using xo::reflect::TypeDescr; using xo::reflect::TaggedPtr; using xo::reflect::TaggedRcptr; using xo::print::quoted; using xo::time::iso8601; using xo::ref::rp; using xo::xtag; namespace json { TaggedRcptr PrintJson::self_tp() { return Reflect::make_rctp(this); //return TaggedRcptr::make(this); } /*self_tp*/ void JsonPrinter::report_internal_type_consistency_error(TypeDescr td1, TypeDescr td2, std::ostream * p_os) const { *p_os << "canonical_name()) << xtag("S", td2->canonical_name()) << ">"; } /*report_internal_type_consistency_error*/ namespace { /* this will be used when TaggedPtr refers to a pointer-like value, * e.g. * xo::ref::rp */ void print_generic_pointer(PrintJson const & print_json, TaggedPtr tp, std::ostream * p_os) { /* e.g. if * xo::ref::rp opt = ...; * then expect to print just as we would for * VanillaOption & opt = ...; * if pointer is null, will print {} */ if (tp.n_child()) { print_json.print_aux(tp.get_child(0), p_os); } else { /* note: this can be distinguished from a bona fide struct, * b/c it doesn't supply the _name_ member */ *p_os << "{}"; } } /*print_generic_pointer*/ /* this will be used when TaggedPtr refers to a vector-like value, * e.g. * std::vector * std::array */ void print_generic_vector(PrintJson const & print_json, TaggedPtr tp, std::ostream * p_os) { /* e.g. if * std::array v{1, 2, 3}; * * then expect to print * [1.0, 2.0, 3.0] */ *p_os << "["; for (uint32_t i = 0, n = tp.n_child(); i < n; ++i) { if (i > 0) *p_os << ", "; print_json.print_aux(tp.get_child(i), p_os); } *p_os << "]"; } /*print_generic_vector*/ /* this will be used when TaggedPtr is understood to refer to a struct-like value. */ void print_generic_struct(PrintJson const & print_json, TaggedPtr tp, std::ostream * p_os) { /* e.g. if * struct Foo { int x_; double y_; }; * Foo foo{1, 1.4142}; * * then expect to print * {"_name_": "Foo", "x": 1, "y": 1.4142} * * note that python json parser requires property names in double quotes */ *p_os << "{"; *p_os << "\"_name_\": \"" << tp.td()->short_name() << "\""; for (uint32_t i = 0, n = tp.n_child(); i < n; ++i) { *p_os << ", \"" << tp.struct_member_name(i) << "\": "; print_json.print_aux(tp.get_child(i), p_os); } *p_os << "}"; } /*print_generic_struct*/ } /*namespace*/ void PrintJson::print_aux(TaggedPtr tp, std::ostream * p_os) const { if (tp.td()) { TypeId id = tp.td()->id(); std::unique_ptr const * printer = this->printer_map_.lookup(id); if (printer && *printer) { (*printer)->print_json(tp, p_os); } else { /* if no special-case printer, apply generic printing behavior */ switch (tp.td()->metatype()) { case Metatype::mt_pointer: print_generic_pointer(*this, tp, p_os); return; case Metatype::mt_vector: print_generic_vector(*this, tp, p_os); return; case Metatype::mt_struct: print_generic_struct(*this, tp, p_os); return; case Metatype::mt_invalid: case Metatype::mt_atomic: break; } (*p_os) << "canonical_name()) << xtag("metatype", tp.td()->metatype()) << ">"; } } else { (*p_os) << ""; } } /*print_aux*/ void PrintJson::print_tp(TaggedPtr tp, std::ostream * p_os) const { this->print_aux(tp, p_os); //*p_os << std::ends; } /*print*/ void PrintJson::print_obj(rp const & obj, std::ostream * p_os) const { assert(obj.get()); this->print_tp(obj->self_tp(), p_os); } /*print_obj*/ /* Consider: * TaggedPtr tp = ...; * std::ostream * p_os = ...; * * PrintJson * pjson = PrintJsonSingleton::instance(); * * // print json representation, depending on runtime type of tp's target * pjson->print_tp(tp, p_os); * * // can also use .print(), relying on JsonPrinter_TaggedPtr * // .print() will next original TaggedPtr in another; * // this shim unwinds that * // * pjson->print(tp, p_os); */ class JsonPrinter_TaggedPtr : public JsonPrinter { public: JsonPrinter_TaggedPtr(PrintJson const * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { TaggedPtr * x = this->check_recover_native(tp, p_os); if (x) { this->pjson()->print_tp(*x, p_os); } } /*print_json*/ }; /*JsonPrinter_TaggedPtr*/ namespace { void provide_tagged_ptr_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_TaggedPtr(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_tagged_ptr_printer*/ } /*namespace*/ class JsonPrinter_bool : public JsonPrinter { public: JsonPrinter_bool(PrintJson const * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { bool * x = this->check_recover_native(tp, p_os); if (x) { /* json boolean format is lower case true/false. * (note that this conflicts with python True/False, achtung!) */ *p_os << (*x ? "true" : "false"); } } /*print_json*/ }; /*JsonPrinter_bool*/ namespace { void provide_bool_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_bool(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_bool_printer*/ } /*namespace*/ template class JsonPrinter_integer : public JsonPrinter { public: JsonPrinter_integer(PrintJson const * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { T * x = tp.recover_native(); if (x) { *p_os << *x; } else { report_internal_type_consistency_error(Reflect::require(), tp.td(), p_os); } } /*print_json*/ }; /*JsonPrinter_integer*/ namespace { template void provide_integer_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_integer(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_integer_printer*/ } template class JsonPrinter_floatingpoint : public JsonPrinter { public: JsonPrinter_floatingpoint(PrintJson const * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { T * x = tp.recover_native(); if (x) { if (std::isfinite(*x)) { *p_os << *x; } else { /* special cases. * use javascript-friendly format * * Note non-finite floating-point values are not representable in * standard json (?!#), though it's a standard extension */ if (std::isnan(*x)) *p_os << "NaN"; else if (*x > 0.0) *p_os << "Infinity"; else *p_os << "-Infinity"; } } else { report_internal_type_consistency_error(Reflect::require(), tp.td(), p_os); } } /*print_json*/ }; /*JsonPrinter_floatingpoint*/ namespace { template void provide_floatingpoint_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_floatingpoint(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_floatingpoint_printer*/ } /*namespace*/ template class JsonPrinter_string : public JsonPrinter { public: JsonPrinter_string(PrintJson const * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { T * x = tp.recover_native(); if (x) { /* TODO: escapes special characters */ *p_os << quoted(*x); } else { report_internal_type_consistency_error(Reflect::require(), tp.td(), p_os); } } /*print_json*/ }; /*JsonPrinter_string*/ namespace { template void provide_string_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_string(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_string_printer*/ } /*namespace */ class JsonPrinter_utc_nanos : public JsonPrinter { public: JsonPrinter_utc_nanos(PrintJson * pjson) : JsonPrinter(pjson) {} virtual void print_json(TaggedPtr tp, std::ostream * p_os) const override { utc_nanos * x = tp.recover_native(); if (x) { /* format like * "2012-04-23T18:25:43.511Z" * since that's what javascript uses */ *p_os << "\"" << iso8601(*x) << "\""; } else { report_internal_type_consistency_error(Reflect::require(), tp.td(), p_os); } } /*print_json*/ }; /*JsonPrinter_utc_nanos*/ namespace { void provide_utc_nanos_printer(PrintJson * p_json) { std::unique_ptr printer(new JsonPrinter_utc_nanos(p_json)); p_json->provide_printer(Reflect::require(), std::move(printer)); } /*provide_utc_nanos_printer*/ } /*namespace*/ PrintJson::PrintJson() { this->provide_std_printers(); } /*ctor*/ /* provide printers for common basic types */ void PrintJson::provide_std_printers() { provide_tagged_ptr_printer(this); provide_bool_printer(this); provide_integer_printer(this); provide_integer_printer(this); provide_integer_printer(this); provide_integer_printer(this); provide_integer_printer(this); provide_integer_printer(this); provide_floatingpoint_printer(this); provide_floatingpoint_printer(this); provide_string_printer(this); provide_string_printer(this); provide_string_printer(this); provide_string_printer(this); provide_utc_nanos_printer(this); } /*provide_std_printers*/ rp PrintJsonSingleton::s_instance; rp PrintJsonSingleton::instance() { if (!s_instance) s_instance = new PrintJson(); return s_instance; } /*instance*/ } /*namespace json*/ } /*namespace xo*/ /* end JsonPrinter.cpp */