Format certain JSON objects on one line
<p>Consider the following code:</p>

<pre><code>&gt;&gt;&gt; import json
&gt;&gt;&gt; data = {
...     'x': [1, {'$special': 'a'}, 2],
...     'y': {'$special': 'b'},
...     'z': {'p': True, 'q': False}
... }
&gt;&gt;&gt; print(json.dumps(data, indent=2))
{
  "y": {
    "$special": "b"
  },
  "z": {
    "q": false,
    "p": true
  },
  "x": [
    1,
    {
      "$special": "a"
    },
    2
  ]
}
</code></pre>

<p>What I want is to format the JSON so that JSON objects that have only a single property <code>'$special'</code> are rendered on a single line, as follows.</p>

<pre><code>{
  "y": {"$special": "b"},
  "z": {
    "q": false,
    "p": true
  },
  "x": [
    1,
    {"$special": "a"},
    2
  ]
}
</code></pre>

<p>I have played around with implementing a custom <a href="https://docs.python.org/3/library/json.html#json.JSONEncoder" rel="nofollow"><code>JSONEncoder</code></a> and passing that in to <code>json.dumps</code> as the <code>cls</code> argument, but the two methods on <code>JSONEncoder</code> each have a problem:</p>

<ul>
<li><p>The <code>JSONEncoder</code> <a href="https://docs.python.org/3/library/json.html#json.JSONEncoder.default" rel="nofollow"><code>default</code></a> method is called for each part of <code>data</code>, but the return value is not a raw JSON string, so there doesn't appear to be any way to adjust its formatting.</p></li>
<li><p>The <code>JSONEncoder</code> <a href="https://docs.python.org/3/library/json.html#json.JSONEncoder.encode" rel="nofollow"><code>encode</code></a> method does return a raw JSON string, but it is only called once for the <code>data</code> as a whole.</p></li>
</ul>

<p>Is there any way I can get <code>JSONEncoder</code> to do what I want?</p>

<p>I found the following regex-based solution to be simplest, albeit &hellip; <em>regex-based</em>.</p>

<pre><code>import json
import re
data = {
    'x': [1, {'$special': 'a'}, 2],
    'y': {'$special': 'b'},
    'z': {'p': True, 'q': False}
}
text = json.dumps(data, indent=2)
pattern = re.compile(r"""
{
\s*
"\$special"
\s*
:
\s*
"
((?:[^"]|\\"))*  # Captures zero or more NotQuote or EscapedQuote
"
\s*
}
""", re.VERBOSE)
print(pattern.sub(r'{"$special": "\1"}', text))
</code></pre>

<p>The output follows.</p>

<pre><code>{
  "x": [
    1,
    {"$special": "a"},
    2
  ],
  "y": {"$special": "b"},
  "z": {
    "q": false,
    "p": true
  }
}
</code></pre>

python json python-3.x formatting