TypoScript cObjects 

EXT:headless registers a handful of new cObjects:

  • JSON
  • CONTENT_JSON
  • BOOL, FLOAT, INT

BOOL, FLOAT and INT take value and value. (stdWrap applied to the value); every remaining top-level property is treated as stdWrap configuration directly — noIndex.field = no_index works, there is no stdWrap. sub-key like core TEXT. They return a real bool / float / int and only work as fields inside a JSON cObject, not in generic TypoScript.

JSON 

Builds a JSON object inline.

lib.meta = JSON
lib.meta {
  if.isTrue = 1
  fields {
    title = TEXT
    title {
      field = seo_title
      stdWrap.ifEmpty.cObject = TEXT
      stdWrap.ifEmpty.cObject {
        field = title
      }
    }
    robots {
      fields {
        noIndex = BOOL
        noIndex.field = no_index
      }
    }
    ogImage = TEXT
    ogImage {
      dataProcessing {
        10 = FriendsOfTYPO3\Headless\DataProcessing\FilesProcessor
        10 {
          as = media
          references.fieldName = og_image
          processingConfiguration {
            returnFlattenObject = 1
          }
        }
      }
    }
  }
  dataProcessing {
  }
  stdWrap {
  }
}
Copied!

The JSON cObject understands these properties:

`if` — render the object only when the condition is met.

`fields` — array of child cObjects. Each field accepts:

  • intval / floatval / boolval — cast the result.
  • ifEmptyReturnNull — return null when the result is empty.
  • ifEmptyUnsetKey — drop the key when the result is empty.
  • source — nested field blocks only: output the block's fields result under a different key. Ignored on plain fields, and ignored when the block defines dataProcessing — that result is always stored under the block's own key.
  • dataProcessing — run data processors (see lib.meta.ogImage).

`nullableFieldsIfEmpty` — comma list of field names to null out when empty (bulk variant of ifEmptyReturnNull).

A field whose cObject is USER_INT (or whose output starts with an <!--INT_SCRIPT placeholder) is wrapped in HeadlessUserInt markers, so the uncacheable value is substituted into the JSON on output; with ifEmptyReturnNull = 1 the nullable marker variant is used.

`dataProcessing`replaces the fields output: the processors run and the value registered under the last as key becomes the object's content (e.g. MenuProcessor). Set dataProcessingMerge to keep both.

`dataProcessingMerge` — merge instead of replace. With dataProcessingMerge = 1 and fields present, the fields output is kept and every processor result is added to it under the processor's target (as) name; on a key collision the processor result wins.

lib.page = JSON
lib.page {
  dataProcessingMerge = 1
  fields {
    title = TEXT
    title.field = title
  }
  dataProcessing {
    10 = FriendsOfTYPO3\Headless\DataProcessing\MenuProcessor
    10.as = mainMenu
  }
}

# {"title":"…","mainMenu":[…]} instead of the menu replacing the title
Copied!

The flag also works on a nested field block that defines both fields and dataProcessing. Without the flag — or without fields — the behaviour is unchanged: the processors replace the whole object.

`stdWrap`stdWrap applied to the already-encoded JSON string.

CONTENT_JSON 

Like core's CONTENT, but content elements are grouped by colPos and JSON-encoded by default. All CONTENT options apply, plus four JSON-specific extras:

merge

Run a second CONTENT_JSON query and merge the result into the first — handy for the slide feature.

lib.content = CONTENT_JSON
lib.content {
  table = tt_content
  select {
    orderBy = sorting
    where = {#colPos} != 1
  }
  merge {
    table = tt_content
    select {
      orderBy = sorting
      where = {#colPos} = 1
    }
    slide = -1
  }
}
Copied!

doNotGroupByColPos

0 (default) groups by colPos — every rendered element must then expose a colPos field, otherwise rendering throws a RuntimeException. 1 returns a flat JSON array.

An empty result is encoded as {} with grouping enabled and as [] with doNotGroupByColPos = 1 — consumers must handle both types.

lib.content = CONTENT_JSON
lib.content {
  table = tt_content
  select {
    orderBy = sorting
    where = {#colPos} != 1
  }
  doNotGroupByColPos = 1
}
Copied!

sortByBackendLayout

Order the colPos groups by the order of columns in the page's backend layout instead of numerically.

returnSingleRow

Return the first matched element as a single object instead of an array — for one-record queries. Only takes effect together with doNotGroupByColPos = 1; with the default colPos grouping the flag is silently ignored.

lib.header = CONTENT_JSON
lib.header {
  table = tt_content
  select {
    orderBy = sorting
    max = 1
  }
  doNotGroupByColPos = 1
  returnSingleRow = 1
}
Copied!