How to store and return json in a (custom) post meta field

I’m not exactly sure what are you really trying to do, but anyway, I hope these help?

  • If you send the meta value as an object, e.g. video: {"foo":"bla blah","bar":1234} in JS, then note that WordPress will serialize the value (or convert the object to a serialized string) before it’s saved to the database.

    See update_metadata() which is used by the REST API when updating a meta.

  • So if you set the type to object, then you should send the meta value as an object (or that will evaluate as an object in WordPress/PHP), but as for how, it depends on how you’re making the REST API request, but basically, make sure the request body uses the proper format and the request also needs to set the proper Content-Type header.

    E.g. Using the native fetch() function in JS:

    fetch( 'https://example.com/wp-json/wp/v2/posts', {
        method: 'POST',
        headers: {
            // be sure to send/use the correct Content-Type
            'Content-Type': 'application/json',
    
            // and (cookie) nonce
            'X-WP-Nonce': 'YOUR_NONCE_HERE'
        },
        body: JSON.stringify( { // send a JSON-encoded request body
            title: '\'video\' meta test',
            content: 'test via JS fetch()',
            meta: {
                video: { // pass an object
                    foo: 'bla blah',
                    bar: 1234
                }
            } // end meta
        } ) // end body
    } ) // wrapped
        .then( res => res.json() )
        .then( data => console.log( data.id, data.meta ) );
    

    Or using cURL command line and an application password:

    curl -X POST https://example.com/wp-json/wp/v2/posts --user "YOUR_USERNAME:YOUR_APPLICATION_PASSWORD" -H "Content-Type: application/json" -d "{\"title\":\"'video' meta test\",\"content\":\"test via curl\",\"meta\":{\"video\":{\"foo\":\"bla blah\",\"bar\":1234}}}"

  • If you actually wanted the meta value be saved as a JSON-encoded string, i.e. in the database, the value would look like {"foo":"bla blah","bar":1234} (instead of a serialized string), then you should set the type to string and send the meta value as a string, e.g. (note the single quotes, i.e. ') video: '{"foo":"bla blah","bar":1234}' in JS.

    But if you want the meta value be represented as an object in the REST API response (body), then you can use the prepare_callback argument (see WP_REST_Meta_Fields::prepare_value()) to convert the JSON string to an object, like so:

    register_post_meta(
        'post',  // post type
        'video', // meta key
        array(
            'single'       => true,
            'show_in_rest' => array(
                'type'             => 'string',
                'prepare_callback' => function ( $value ) {
                    return is_object( $value ) ? $value : json_decode( $value );
                },
            ),
        )
    );
    

    Just remember that you would need to, e.g. from a PHP template, manually convert the value to an object after fetching the value using get_post_meta() (or get_metadata()). E.g.

    $post_id = 123;
    $value   = get_post_meta( $post_id, 'video', true ); // true = gets a single value
    $video   = is_object( $value ) ? $value : json_decode( $value );