Oddity with juce::JSON::toString(var)

in juce::JSONFormatter::write there exists this jassert:

        else
        {
            // Can't convert these other types of object to JSON!
            jassert (! (v.isMethod() || v.isBinaryData()));

            out << v.toString();
        }

Yet, when v.isBinaryData() is true and v.toString() is called, this line of code is eventually executed:

    static String       binaryToString (const ValueUnion& data)            
{ return data.binaryValue->toBase64Encoding(); }

the jassert that v not store a MemoryBlock makes no sense.
Why is it there?
MemoryBlock’s are easily convertible to juce::String via the Base64 encoding functions…

I think the real problem is the other way, how do you know for sure it’s a memory block your are deserializing?

It’s always a memory block based on how the var is constructed:

var::var (const void* v, size_t sz)   : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v, sz); }
var::var (const MemoryBlock& v)       : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v); }

These are the only two constructors that convert to binary type.

The JSON format supports a few kinds of values:

  • strings
  • numbers (doubles)
  • true
  • false
  • null
  • objects
  • arrays

There’s no way of expressing ‘binary data’ directly in JSON, which is what the assertion is trying to tell you. You can write a base64 string, but then the program reading that string has to know to that it should be interpreted as data-in-base64, rather than as a plain string.

If you need to put some arbitrary binary into a JSON structure, I’d recommend explicitly encoding/decoding using the Base64 helper functions. That is, no var that you write to a JSON file should hold a MemoryBlock - you should create a new var holding a base64-encoded string instead.

1 Like

ah, ok. that makes sense.

I am working on porting a JavaScript library that handles parsing and loading PEM files (think RSA keys).
they do stuff like this in it:

    var msg = {
      type: type,
      procType: null,
      contentDomain: null,
      dekInfo: null,
      headers: [],
      body: forge.util.decode64(match[3])
    };

I’ve been storing that body as a MemoryBlock wrapped in a var inside of a DynamicObject.
They use the ‘ByteStringBuffer’ class which stores bytes in a stringified format, using UTF16 encoding.
it’s annoying to port over, but I’m getting through it.

If you’re curious, it’s here:

It’s a partial port of this framework, which is a fork of this: GitHub - digitalbazaar/forge: A native implementation of TLS in Javascript and tools to write crypto-based and network-heavy webapps


the CPP equivalent of that JS looks like this:

        //remove all \r\n from msg.body
        auto base64Text = regexMatch[3];
        base64Text = base64Text.removeCharacters("\r\n");
#if false
        DBG( base64Text.length() );
#endif
        using NV = juce::NamedValueSet::NamedValue;
        juce::MemoryBlock bodyBlock;
        {
            juce::MemoryOutputStream mos(bodyBlock, false);
            bool successfulConversion = juce::Base64::convertFromBase64(mos, base64Text);
            if(! successfulConversion )
            {
                jassertfalse;
            }
        }

        juce::NamedValueSet msg
        {
            NV("type", type),
            NV("procType", {}), //empty juce::var
            NV("contentDomain", {}),
            NV("dekInfo", {}),
            NV("headers", juce::Array<juce::var>()),
            NV("body", bodyBlock)
        };