Skip to content

Feature 10: Interchange Formats

Validation | Home →


UWS documents are valid JSON, YAML, or canonical HCL. The convert package in github.com/OpenUdon/uws moves documents between all three formats with round-trip guarantees.

Three Formats, One Document

Format Best for Extensions preserved
JSON Machine interchange, API responses, LLM output
YAML Human authoring, configuration files
HCL Canonical authoring for runtime tooling ✓ via extensions { ... } blocks

Example 1: The Same Operation in All Three Formats

The following is semantically identical in JSON, YAML, and HCL:

JSON

{
  "operationId": "list_pets",
  "sourceDescription": "petstore_api",
  "openapiOperationId": "listPets",
  "request": {
    "query": { "limit": 10, "status": "available" },
    "header": { "X-Trace-Id": "abc-123" }
  },
  "outputs": {
    "firstPet": "$response.body#/0",
    "total": "$response.body.total"
  }
}

YAML

operationId: list_pets
sourceDescription: petstore_api
openapiOperationId: listPets
request:
  query:
    limit: 10
    status: available
  header:
    X-Trace-Id: abc-123
outputs:
  firstPet: $response.body#/0
  total: $response.body.total

HCL

operation "list_pets" {
  sourceDescription  = "petstore_api"
  openapiOperationId = "listPets"

  request = {
    query  = { limit = 10, status = "available" }
    header = { "X-Trace-Id" = "abc-123" }
  }

  outputs = {
    firstPet = "$response.body#/0"
    total    = "$response.body.total"
  }
}

Example 2: A Full Document in All Three Formats

A complete minimal document with a workflow:

YAML (most readable for authoring)

uws: "1.1.0"
info:
  title: Pet Workflow
  version: 1.0.0
sourceDescriptions:
  - name: petstore_api
    url: ./petstore.yaml
    type: openapi
operations:
  - operationId: list_pets
    sourceDescription: petstore_api
    openapiOperationId: listPets
    request:
      query:
        limit: 5
    outputs:
      first: $response.body#/0/id
workflows:
  - workflowId: main
    type: sequence
    steps:
      - stepId: fetch
        operationRef: list_pets

HCL (canonical authoring form)

uws  = "1.1.0"

info {
  title   = "Pet Workflow"
  version = "1.0.0"
}

sourceDescription "petstore_api" {
  url  = "./petstore.yaml"
  type = "openapi"
}

operation "list_pets" {
  sourceDescription  = "petstore_api"
  openapiOperationId = "listPets"
  request = { query = { limit = 5 } }
  outputs = { first = "$response.body#/0/id" }
}

workflow "main" {
  type = "sequence"
  step "fetch" {
    operationRef = "list_pets"
  }
}

Example 3: Switch Case in HCL

Structural constructs translate naturally:

YAML

workflows:
  - workflowId: route
    type: switch
    cases:
      - name: premium
        when: $outputs.tier == "premium"
        steps:
          - stepId: fast_track
            operationRef: express_process
    default:
      - stepId: standard
        operationRef: normal_process

HCL

workflow "route" {
  type = "switch"

  case "premium" {
    when = "$outputs.tier == \"premium\""
    step "fast_track" {
      operationRef = "express_process"
    }
  }

  default {
    step "standard" {
      operationRef = "normal_process"
    }
  }
}

The convert Package

All conversion helpers live in github.com/OpenUdon/uws/convert:

// Between byte slices (raw format conversion)
jsonOut, _ := convert.YAMLToJSON(yamlData)
yamlOut, _ := convert.JSONToYAML(jsonData)
hclOut,  _ := convert.JSONToHCL(jsonData)
jsonOut, _ = convert.HCLToJSON(hclData)

// Marshal a Document struct to bytes
jsonBytes, _ := convert.MarshalJSON(doc)
yamlBytes, _ := convert.MarshalYAML(doc)
hclBytes,  _ := convert.MarshalHCL(doc)

// Unmarshal bytes into a Document struct
convert.UnmarshalJSON(jsonData, &doc)
convert.UnmarshalYAML(yamlData, &doc)
convert.UnmarshalHCL(hclData, &doc)

Example 4: Format Conversion in a Go Program

Read a YAML workflow, validate it, and write it back as JSON for machine consumption:

package main

import (
    "log"
    "os"

    "github.com/OpenUdon/uws/convert"
    "github.com/OpenUdon/uws/uws1"
)

func main() {
    yamlData, _ := os.ReadFile("workflow.uws.yaml")

    var doc uws1.Document
    if err := convert.UnmarshalYAML(yamlData, &doc); err != nil {
        log.Fatal(err)
    }
    if err := doc.Validate(); err != nil {
        log.Fatal(err)
    }

    jsonData, err := convert.MarshalJSONIndent(&doc, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    os.WriteFile("workflow.uws.json", jsonData, 0644)
}

HCL Extensions

HCL represents object-level x-* extension fields inside an extensions block:

operation "render" {
  extensions {
    x-uws-operation-profile = "uws.runtime.1.0"
    x-uws-runtime {
      type     = "fnct"
      function = "identity"
    }
  }
}

When converted to JSON or YAML, these fields flatten back to normal x-* properties on the owning object.

The same rule applies to the public runtime supplement. A JSON/YAML x-uws-runtime object becomes an HCL block inside extensions, and conversion back to JSON/YAML restores the flattened extension field. The runtime supplement payload itself still follows its schema: type is required, and HTTP/OpenAPI calls are represented by core OpenAPI binding fields rather than x-uws-runtime.

$-Key Rewriting for HCL

JSON Schema keys like $ref, $id, $defs are not valid HCL identifiers. The package rewrites them symmetrically in both directions:

JSON / YAML key HCL key
$ref _ref
$id _id
$schema _schema
$defs _defs
$customKey __dollar__customKey

A ParamSchema with $ref in YAML and HCL:

# YAML
inputs:
  _ref: "#/components/schemas/OrderInput"
# HCL — $ref becomes _ref
inputs = { _ref = "#/components/schemas/OrderInput" }

Round-tripping through HCL → JSON restores $ref exactly.

Round-Trip Guarantee

For any core-only (extension-free) UWS document:

JSON → HCL → JSON  produces a structurally identical document
YAML → HCL → YAML  produces a structurally identical document

MarshalHCL works on a deep copy — the caller's document is never mutated during conversion.

From The Big Fixture

The large fixture keeps JSON and HCL versions of the same generated document. This compact JSON excerpt corresponds to the HCL operation examples on the feature pages:

{
  "operationId": "fetch_ticket",
  "sourceDescription": "incident_api",
  "openapiOperationId": "getIncident",
  "request": {
    "path": {
      "incidentId": "$inputs.incidentId",
      "tenantId": "$inputs.tenantId"
    },
    "query": {
      "depth": "full",
      "include": ["timeline", "assets"]
    }
  },
  "outputs": {
    "severity": "$response.body.severity",
    "ticket": "$response.body"
  }
}

Full context: testdata/big/big.json and testdata/big/big.hcl.


Validation | Home →