Actually, an if nginx directive, when being used in location context, has a very little in common with similar constructions from imperative programming languages. One of the best explanations of how this directive actually works was provided by Yichun Zhang, the author of the famous lua-nginx-module and the OpenResty bundle. In fact, every if directive implicitly creates a nested location that tries to inherit all the declarations from the parent location. There are, however, certain limitations on which directives can be used in the so-called "if in location" context (if they can, it will be explicitly stated in documentation), and you have just encountered one of them (the autoindex directive cannot be used in this context). The most common solution to such situations is to use variables as arguments for these directives.
Unfortunately, not all nginx directives allow the use of variables as their parameters (as a rule, if this is possible, it is explicitly stated in the documentation for the corresponding directive). In particular, the autoindex directive does not support specifying its parameter as a variable whose value could be either on or off. Therefore, indeed, the solution involving the use of two separate named locations with different configurations for this directive is most likely the only possible option. However, as I already said, you can't jump to the named location via rewrite directive. Instead, you can do it via the try_files directive, as described here:
map $arg_listing $autoindex { true @autoindex_on; default @autoindex_off; } server { ... listen, server_name, root and other common "server" block parameters here location / { try_files "" $autoindex; } location @autoindex_on { autoindex on; autoindex_format json; } location @autoindex_off { return 403; } ... your other locations here }
But keep in mind, that in order for autoindex directive to take effect, there must not be an index file (index.html by default) in the requested web server directory.