While developing libsigmf (
https://github.com/deepsig/libsigmf) I went through a similar experience. The objective was to have painless json file/string <-> real c++ type (this means the serialization thing had to generate a slimmed down type that is effectively a struct of vector, int, float, etc). I looked at cap'n proto, flatbuffers, protobuf, and some others that weren't as mature/supported. I actually started with cap'n proto and had to switch to flatbuffers.
Other bonuses I encountered were the whole "zero-time serialization" (In my experience people love making fun of cap'n proto for this, but it's true for all of the popular architectures and then you pay for shuffling bytes on more exotic architectures) and the sort of random-access parsing. A big perk of cap'n proto was it actually came with json-parsing to flatbuffer type *and* had the ability to generate a sane struct definition.
I eventually switched to flatbuffers. Here's a randomly-ordered list of why
* cap'n proto's author is no doubt an incredible programmer. However, he made some really weird choices. Like wrote his own string class in libkj and made all of cap'n proto depend on it. And it's not all that friendly with c++ string. So you wind up having to convert every single string in to a kj::string just for serialization (and vice versa)
* libkj also implements its own memory allocators, filesystem library (ala boost filesystem / c++ filesystem), mutexes, and threads. I'm not capable of vetting those implementation but beg to be asked a big old "WHY?!"
* cap'n proto is incredibly strict (in CS parlance I guess you can call it opinionated) on protocol description language. It enforces things such as FirstletterMustBeCapital
* I also felt the syntax of moving around these objects was a bit awkward
I think there may be some other less appalling things that I've struck from my memory as "writing your own string that's incompatible with the rest of c++" is a big enough grievance that I'm only just now starting to forgive gcc dual abi for (but at least even that is at the ABI level which is always hard to get right). Flatbuffers offered pretty much the same promises as cap'n proto but without the "reinvent the standard library first" mindset and let's you make your own choices about how to name your fields, etc. I will say cap'n proto had a nice-looking extension capability to their parsing which I haven't found a good alternative for.
The downside is flatbuffers is released by google/android's benevolence and somewhat competes with protobufs which is clearly their darling. I can't tell how much anyone else cares about flatbuffers although the *major* advantage to flatbuffers that I couldn't find in protobuf (which made me choose flatbuffers) is the struct code generator so you get out pretty normal struct's from their code generator that look like something you would write if you did it by hand.
The downside is I don't know what complex<T> support looks like in any of these things.
Cheers,
Nathan