Skip to content

Qt6 JSON

Qt6Json.natvis contains NatVis expressions for Qt 6's JSON types. In particular, the following types (excluding CBOR) are visualized:

Iterators (possibly const) and QJson{Array,Object}(Const)Ref (potentially in 6.9) are automatically visualized as they wrap types with existing visualizers.

The visualizers are based on QJson.natvis for Qt 5 from Aleksey Nikolaev. They were extended to support both UTF-16 and UTF-8 (+ASCII) strings. Additionally, there's basic support for CBOR types (QCborValue etc.).

To add the visualizers to Visual Studio, follow the documentation. In VS Code, use visualizerFile. Note that only one file is supported right now, so you might have to merge this with other visualizers.

Warning

Make sure that symbols for QtCore are loaded (i.e. Qt6Cored.pdb), otherwise, the expression evaluator will fail to expand variables (error similar to: Error while evaluating '(Qt6Cored.dll!QCborContainerPrivate*)o.d' in the context of type 'something.exe!QJsonObject'). When installing Qt through the online installer, make sure to select Qt Debug Information Files.

In your debugger, add the bin directory of your Qt installation (e.g. C:\Qt\6.8.0\msvc2022_64\bin) to the symbol search path. For Visual Studio, this is located in Options > Debugging > Symbols. In VS Code, set symbolOptions.searchPaths in your launch-configuration.

The visualizers currently only support dynamically linked debug builds of Qt. To support release builds, replace Qt6Cored.dll with Qt6Core.dll in the NatVis file. In a statically linked build, it should be enough to remove Qt6Cored.dll! (I didn't test this yet).

If you see any bug or have additions, feel free to open issues and PRs in the repo at the top of this page.

natvis/Qt6Json.natvis
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
  <Type Name="QJsonDocument">
    <DisplayString Condition="!d._Mypair._Myval2">empty</DisplayString>
    <Expand>
      <ExpandedItem Condition="d._Mypair._Myval2">
        ((Qt6Cored.dll!QJsonDocumentPrivate*)(d._Mypair._Myval2))-&gt;value
      </ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QJsonArray">
    <Intrinsic Name="cbor" Expression="(Qt6Cored.dll!QCborContainerPrivate*)a.d"></Intrinsic>
    <Expand>
      <ExpandedItem>cbor(),view(arr)</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QCborArray">
    <Intrinsic Name="cbor" Expression="(Qt6Cored.dll!QCborContainerPrivate*)d.d"></Intrinsic>
    <Expand>
      <ExpandedItem>cbor(),view(arr)</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QJsonObject">
    <Intrinsic Name="cbor" Expression="(Qt6Cored.dll!QCborContainerPrivate*)o.d"></Intrinsic>
    <DisplayString Condition="!cbor()">empty</DisplayString>
    <Expand>
      <ExpandedItem
          Condition="cbor()">cbor(),view(map)</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QCborMap">
    <Intrinsic Name="cbor" Expression="(Qt6Cored.dll!QCborContainerPrivate*)d.d"></Intrinsic>
    <DisplayString Condition="!cbor()">empty</DisplayString>
    <Expand>
      <ExpandedItem
          Condition="cbor()">cbor(),view(map)</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QJsonValue">
    <DisplayString>{value}</DisplayString>
    <Expand>
      <ExpandedItem>value</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QCborValue">
    <DisplayString Condition="t == QCborValue::Null">null</DisplayString>
    <DisplayString Condition="t == QCborValue::Invalid">invalid</DisplayString>
    <DisplayString Condition="t == QCborValue::Undefined">undefined</DisplayString>
    <DisplayString Condition="t == QCborValue::False">false</DisplayString>
    <DisplayString Condition="t == QCborValue::True">true</DisplayString>
    <DisplayString Condition="t == QCborValue::Integer">{n}</DisplayString>
    <DisplayString Condition="t == QCborValue::Double">{*(double*)&amp;n}</DisplayString>

    <DisplayString Condition="t == QCborValue::String &amp;&amp; container-&gt;el(n).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
      {container-&gt;byteDataAt(n)-&gt;u16(),[container-&gt;byteDataAt(n)-&gt;len/2]su}
    </DisplayString>
    <DisplayString Condition="t == QCborValue::String">
      {container-&gt;byteDataAt(n)-&gt;str(),[container-&gt;byteDataAt(n)-&gt;len]s8}
    </DisplayString>

    <DisplayString Condition="(t == QCborValue::DateTime || t == QCborValue::Url || t == QCborValue::RegularExpression) &amp;&amp; container-&gt;el(1).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
      {container-&gt;byteDataAt(1)-&gt;u16(),[container-&gt;byteDataAt(1)-&gt;len/2]su}
    </DisplayString>
    <DisplayString Condition="(t == QCborValue::DateTime || t == QCborValue::Url || t == QCborValue::RegularExpression)">
      {container-&gt;byteDataAt(1)-&gt;str(),[container-&gt;byteDataAt(1)-&gt;len]s8}
    </DisplayString>

    <DisplayString Condition="t == QCborValue::Array &amp;&amp; container == nullptr">[]</DisplayString>
    <DisplayString Condition="t == QCborValue::Map &amp;&amp; container == nullptr">{{}}</DisplayString>

    <Expand>
      <ExpandedItem Condition="t == QCborValue::Array">container,view(arr)na</ExpandedItem>
      <ExpandedItem Condition="t == QCborValue::Map">container,view(map)na</ExpandedItem>
      <ExpandedItem Condition="t == QCborValue::Uuid">container,view(bytesAtOne)na</ExpandedItem>
      <ExpandedItem Condition="t == QCborValue::ByteArray">
        container-&gt;byteDataAt(n)-&gt;bytes(),[container-&gt;byteDataAt(n)-&gt;len]hv
      </ExpandedItem>
      <ExpandedItem Condition="t == QCborValue::String &amp;&amp; container-&gt;el(n).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
        container-&gt;byteDataAt(n)-&gt;u16(),[container-&gt;byteDataAt(n)-&gt;len/2]su
      </ExpandedItem>
      <ExpandedItem Condition="t == QCborValue::String &amp;&amp; !(container-&gt;el(n).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16)">
        container-&gt;byteDataAt(n)-&gt;str(),[container-&gt;byteDataAt(n)-&gt;len]s8
      </ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QCborContainerPrivate">
    <Intrinsic
        Name="self" Expression="(Qt6Cored.dll!QCborContainerPrivate*)this" />
    <Intrinsic
        Name="el" Expression="self()-&gt;elements.d.ptr[n]">
      <Parameter Name="n" Type="long long" />
    </Intrinsic>
    <Intrinsic
        Name="byteDataAt"
        Expression="((Qt6Cored.dll!QtCbor::ByteData*)(self()-&gt;data.d.ptr + el(n).value))">
      <Parameter Name="n" Type="long long" />
    </Intrinsic>

    <Expand>
      <!-- Arrays -->
      <CustomListItems Condition="self()-&gt;elements.d.size &gt; 0" IncludeView="arr">
        <Variable Name="i" InitialValue="0" />
        <Loop Condition="i &lt; self()-&gt;elements.d.size">
          <If Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::HasByteData">
            <If Condition="el(i).type == QCborValue::Type::String">
              <If Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
                <Item Name="[{i}]">byteDataAt(i)-&gt;u16(),[byteDataAt(i)-&gt;len/2]su</Item>
              </If>
              <Else>
                <Item Name="[{i}]">byteDataAt(i)-&gt;str(),[byteDataAt(i)-&gt;len]s8</Item>
              </Else>
            </If>
            <Else>
              <Item Name="[{i}]">byteDataAt(i)-&gt;bytes(),[byteDataAt(i)-&gt;len]hv</Item>
            </Else>
          </If>
          <Elseif Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::IsContainer">
            <If Condition="el(i).type == QCborValue::Type::Array">
              <Item Name="[{i}]">el(i).container,view(arr)na</Item>
            </If>
            <Else>
              <Item Name="[{i}]">el(i).container,view(map)na</Item>
            </Else>
          </Elseif>
          <Else>
            <Item Name="[{i}]">el(i)</Item>
          </Else>
          <Exec>++i</Exec>
        </Loop>
      </CustomListItems>

      <!-- Maps/Objects -->
      <CustomListItems Condition="self()-&gt;elements.d.size &gt; 0" IncludeView="map">
        <!-- keys can be anything in CBOR. Only strings and simple types are supported as keys (others would bloat the file) -->
        <Variable Name="key8" InitialValue="(const char *)0" />
        <Variable Name="key16" InitialValue="(const char16_t *)0" />
        <!-- key8 and key16 might be nullptr even though the key is a string (we can't use "key8 || key16" to check if the key is a string) -->
        <Variable Name="strKey" InitialValue="true"/>
        <Variable Name="len" InitialValue="0" />
        <Variable Name="i" InitialValue="0" />
        <Loop Condition="i &lt; self()-&gt;elements.d.size">
          <If Condition="(i&amp;1) == 0">
            <!-- even elements are keys -->
            <If Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
              <!-- utf16 key -->
              <Exec>key8 = nullptr</Exec>
              <Exec>key16 = byteDataAt(i)-&gt;u16()</Exec>
              <Exec>len = byteDataAt(i)-&gt;len/2</Exec>
              <Exec>strKey = true</Exec>
            </If>
            <Elseif Condition="el(i).type == QCborValue::Type::String">
              <!-- utf8/ascii key -->
              <Exec>key8 = byteDataAt(i)-&gt;str()</Exec>
              <Exec>key16 = nullptr</Exec>
              <Exec>len = byteDataAt(i)-&gt;len</Exec>
              <Exec>strKey = true</Exec>
            </Elseif>
            <Else>
              <!-- non-string key -->
              <Exec>key8 = nullptr</Exec>
              <Exec>key16 = nullptr</Exec>
              <Exec>len = 0</Exec>
              <Exec>strKey = false</Exec>
            </Else>
          </If>
          <Else>
            <!-- odd elements are values -->
            <If Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::HasByteData">
              <If Condition="el(i).type == QCborValue::Type::String">
                <If Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
                  <!-- utf16 strings -->
                  <If Condition="key16">
                    <Item Name="[{key16,[len]su}]">byteDataAt(i)-&gt;u16(),[byteDataAt(i)-&gt;len/2]su</Item>
                  </If>
                  <Elseif Condition="strKey">
                    <Item Name="[{key8,[len]s8}]">byteDataAt(i)-&gt;u16(),[byteDataAt(i)-&gt;len/2]su</Item>
                  </Elseif>
                  <Else>
                    <Item Name="[{el(i-1)}]">byteDataAt(i)-&gt;u16(),[byteDataAt(i)-&gt;len/2]su</Item>
                  </Else>
                </If>
                <Else>
                  <!-- utf8 or ascii strings (assume it's utf8) -->
                  <If Condition="key16">
                    <Item Name="[{key16,[len]su}]">byteDataAt(i)-&gt;str(),[byteDataAt(i)-&gt;len]s8</Item>
                  </If>
                  <Elseif Condition="strKey">
                    <Item Name="[{key8,[len]s8}]">byteDataAt(i)-&gt;str(),[byteDataAt(i)-&gt;len]s8</Item>
                  </Elseif>
                  <Else>
                    <Item Name="[{el(i-1)}]">byteDataAt(i)-&gt;str(),[byteDataAt(i)-&gt;len]s8</Item>
                  </Else>
                </Else>
              </If>
              <Else>
                <!-- elements with byte-data that aren't strings (e.g. byte arrays/uuids) -->
                <If Condition="key16">
                  <Item Name="[{key16,[len]su}]">byteDataAt(i)-&gt;bytes(),[byteDataAt(i)-&gt;len]hv</Item>
                </If>
                <Elseif Condition="strKey">
                  <Item Name="[{key8,[len]s8}]">byteDataAt(i)-&gt;bytes(),[byteDataAt(i)-&gt;len]hv</Item>
                </Elseif>
                <Else>
                  <Item Name="[{el(i-1)}]">byteDataAt(i)-&gt;bytes(),[byteDataAt(i)-&gt;len]hv</Item>
                </Else>
              </Else>
            </If>
            <Elseif Condition="el(i).flags.i &amp; Qt6Cored.dll!QtCbor::Element::IsContainer">
              <If Condition="el(i).type == QCborValue::Type::Array">
                <!-- arrays -->
                <If Condition="key16">
                  <Item Name="[{key16,[len]su}]">el(i).container,view(arr)na</Item>
                </If>
                <Elseif Condition="strKey">
                  <Item Name="[{key8,[len]s8}]">el(i).container,view(arr)na</Item>
                </Elseif>
                <Else>
                  <Item Name="[{el(i-1)}]">el(i).container,view(arr)na</Item>
                </Else>
              </If>
              <Else>
                <!-- maps/objects -->
                <If Condition="key16">
                  <Item Name="[{key16,[len]su}]">el(i).container,view(map)na</Item>
                </If>
                <Elseif Condition="strKey">
                  <Item Name="[{key8,[len]s8}]">el(i).container,view(map)na</Item>
                </Elseif>
                <Else>
                  <Item Name="[{el(i-1)}]">el(i).container,view(map)na</Item>
                </Else>
              </Else>
            </Elseif>
            <Else>
              <!-- simple types (bool/null/undefined/number) -->
              <If Condition="key16">
                <Item Name="[{key16,[len]su}]">el(i)</Item>
              </If>
              <Elseif Condition="strKey">
                <Item Name="[{key8,[len]s8}]">el(i)</Item>
              </Elseif>
              <Else>
                <Item Name="[{el(i-1)}]">el(i)</Item>
              </Else>
            </Else>
          </Else>
          <Exec>++i</Exec>
        </Loop>
      </CustomListItems>

      <ExpandedItem IncludeView="stringAtOne" Condition="el(1).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
        byteDataAt(1)-&gt;u16(),[byteDataAt(1)-&gt;len/2]su
      </ExpandedItem>
      <ExpandedItem IncludeView="stringAtOne" Condition="!(el(1).flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16)">
        byteDataAt(1)-&gt;str(),[byteDataAt(1)-&gt;len]s8
      </ExpandedItem>
      <ExpandedItem IncludeView="bytesAtOne">
        byteDataAt(1)-&gt;bytes(),[byteDataAt(1)-&gt;len]hv
      </ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QtCbor::ByteData">
    <Intrinsic Name="str" Expression="(const char *)(this + 1)" ModuleName="Qt6Cored.dll"></Intrinsic>
    <Intrinsic Name="bytes" Expression="(const uint8_t *)(this + 1)" ModuleName="Qt6Cored.dll"></Intrinsic>
    <Intrinsic Name="u16" Expression="(const char16_t *)(this + 1)" ModuleName="Qt6Cored.dll"></Intrinsic>
  </Type>

  <Type Name="QtCbor::Element">
    <DisplayString Condition="type == QCborValue::Null">null</DisplayString>
    <DisplayString Condition="type == QCborValue::Undefined">undefined</DisplayString>
    <DisplayString Condition="type == QCborValue::False">false</DisplayString>
    <DisplayString Condition="type == QCborValue::True">true</DisplayString>
    <DisplayString Condition="type == QCborValue::Integer">{value}</DisplayString>
    <DisplayString Condition="type == QCborValue::Double">{*(double*)&amp;value}</DisplayString>
    <DisplayString Condition="type == QCborValue::Type::Array &amp;&amp; container == nullptr">[]</DisplayString>
    <DisplayString Condition="type == QCborValue::Type::Map &amp;&amp; container == nullptr">{{}}</DisplayString>
  </Type>

  <Type Name="QJsonDocumentPrivate">
    <Intrinsic
            Name="self" Expression="(Qt6Cored.dll!QJsonDocumentPrivate*)this" />
    <Expand>
      <ExpandedItem>self()-&gt;value</ExpandedItem>
    </Expand>
  </Type>

  <Type Name="QJsonValueRef">
    <Intrinsic Name="container" Expression="(Qt6Cored.dll!QCborContainerPrivate*)(is_object ? o-&gt;o.d : a-&gt;a.d)" />
    <Intrinsic Name="idx" Expression="is_object ? index * 2 + 1 : index" />
    <Intrinsic Name="el" Expression="container()-&gt;el(idx())"/>
    <Intrinsic Name="data" Expression="container()-&gt;byteDataAt(idx())" />
    <Intrinsic Name="ty" Expression="el().type" />

    <DisplayString Condition="ty() == QCborValue::Type::String &amp;&amp; el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
      {data()-&gt;u16(),[data()-&gt;len/2]su}
    </DisplayString>
    <DisplayString Condition="ty() == QCborValue::Type::String">
      {data()-&gt;str(),[data()-&gt;len]s8}
    </DisplayString>
    <DisplayString Condition="ty() != QCborValue::Type::Array &amp;&amp; ty() != QCborValue::Type::Map || !el().container">
      {el()}
    </DisplayString>

    <Expand>
      <ExpandedItem Condition="ty() == QCborValue::Type::Array">el().container,view(arr)na</ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::Map">el().container,view(map)na</ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::String &amp;&amp; el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
        data()-&gt;u16(),[data()-&gt;len/2]su
      </ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::String &amp;&amp; !(el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16)">
        data()-&gt;str(),[data()-&gt;len]s8
      </ExpandedItem>
    </Expand>
  </Type>

  <!-- Same as QJsonValueRef -->
  <Type Name="QJsonValueConstRef">
    <Intrinsic Name="container" Expression="(Qt6Cored.dll!QCborContainerPrivate*)(is_object ? o-&gt;o.d : a-&gt;a.d)" />
    <Intrinsic Name="idx" Expression="is_object ? index * 2 + 1 : index" />
    <Intrinsic Name="el" Expression="container()-&gt;el(idx())"/>
    <Intrinsic Name="data" Expression="container()-&gt;byteDataAt(idx())" />
    <Intrinsic Name="ty" Expression="el().type" />

    <DisplayString Condition="ty() == QCborValue::Type::String &amp;&amp; el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
      {data()-&gt;u16(),[data()-&gt;len/2]su}
    </DisplayString>
    <DisplayString Condition="ty() == QCborValue::Type::String">
      {data()-&gt;str(),[data()-&gt;len]s8}
    </DisplayString>
    <DisplayString Condition="ty() != QCborValue::Type::Array &amp;&amp; ty() != QCborValue::Type::Map || !el().container">
      {el()}
    </DisplayString>

    <Expand>
      <ExpandedItem Condition="ty() == QCborValue::Type::Array">el().container,view(arr)na</ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::Map">el().container,view(map)na</ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::String &amp;&amp; el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16">
        data()-&gt;u16(),[data()-&gt;len/2]su
      </ExpandedItem>
      <ExpandedItem Condition="ty() == QCborValue::Type::String &amp;&amp; !(el().flags.i &amp; Qt6Cored.dll!QtCbor::Element::StringIsUtf16)">
        data()-&gt;str(),[data()-&gt;len]s8
      </ExpandedItem>
    </Expand>
  </Type>
</AutoVisualizer>

View Qt6Json.natvis on GitHub