How can I create an empty JSON object?

You can define your own object and tell Mathematica to interpret it as JSON object.

Dummy export to load relevant contexts:

In[1]:= ExportString["", "JSON"];

Tell Mathematica to interpret JSONObject symbol as possible head of JSON objects:

In[2]:= ClearAll[JSONObject]
         System`Convert`JSONDump`$JSONObjectHead = JSONObject;

Now you can use JSONObject anywhere in exported expression:

In[4]:= ExportString[JSONObject[], "JSON"]
Out[4]= {}

In[5]:= ExportString[{"a" -> JSONObject[]}, "JSON"]
Out[5]= {"a" : {}}

In[6]:= ExportString[JSONObject["a" -> JSONObject["b" -> Null]], "JSON"]
Out[6]= {"a" : {"b" : null}}

How it works

Internally export to JSON is handled by System`Convert`JSONDump`exportJSON function which is just a wrapper for System`Convert`JSONDump`iexportJSON. The latter calls System`Convert`JSONDump`toString which does the real conversion and, among others, includes following definitions (System`Convert`JSONDump` context removed from symbol names):

toString[x_?($JSONObjectHead =!= List && MatchQ[#1, $JSONObjectHead[]]&), `t_Integer] := {}

toString[x_?($JSONObjectHead =!= List && Head[#1] === $JSONObjectHead && Length[#1] =!= 0&), t_Integer] := toString[List @@ x, t]

that use $JSONObjectHead.

How to find out, how exporting works

You can start by tracing evaluation of export expression:

TracePrint[
    ExportString["mySpecialString", "JSON"],
    _[___, "mySpecialString", ___]
]

You'll find that System`Convert`JSONDump` context looks interesting. For fast overview of it's symbols you can look at:

Names["System`Convert`JSONDump`*"] // TableForm

Real fun will start if you use this great spelunking tool:

Get["https://raw.githubusercontent.com/szhorvat/Spelunking/master/Spelunking.m"]

Pick a function, from printed trace that looks promising:

Spelunk["System`Convert`JSONDump`exportJSON"]

and dig deeper by clicking on links to functions that you see in the trace.

About spelunking tool I learned from one of halirutans answers.


For newer versions exporting an empty Association does what you want:

ExportString[<||>, "JSON"]

Newer Versions means >= 10.1, 10.0.x versions did export empty Associations to empty lists, of course the new behavior is a much better match for the distinction of JavaScript empty lists and objects in Mathematica...

As mentioned by Kuba in a comment the export format "JSON" has some deficiancies which are overcome by the export format "RawJSON" which seem to work better than "JSON" in many respects, especially when working with Associations...

Kubas example where "JSON" gives an error while "RawJSON" works as intendes was:

ExportString[<|"test" -> <||>|>, "RawJSON"]