YAML Construction Guide
This guide provides comprehensive instructions for building YAML files that Survey123Py can convert to Survey123-compatible Excel files.
Overview
Survey123Py uses YAML files to define surveys in a human-readable format, which are then converted to Excel files with proper Survey123 formatting. Each YAML file contains three main sections:
settings: Form configuration and metadata
choices: Choice lists for select questions
survey: The actual survey questions and structure
Understanding the “-” Syntax
The YAML “-” (dash) syntax indicates a list item. In Survey123Py:
Each “-” in the survey section represents a single row in the Excel survey sheet
Each “-” in the choices section represents a single row in the Excel choices sheet
The fields within each list item map to specific Excel columns
Example:
survey:
- type: text # First row. "text" will be written to the cell in the "type" column in Excel
name: q1 # "q1" will be written in the "name" column
label: Your name? # "Your name?" will be written in the "label" column
- type: integer # This creates a second row in Excel
name: q2
label: Your age?
Excel Column Mappings
Survey Sheet Columns
Each question (marked with “-”) in the survey section maps to these Excel columns:
YAML Field |
Excel Column |
Description |
|---|---|---|
type |
A |
Question type (text, integer, select_one, etc.) |
name |
B |
Unique identifier for the question |
label |
C |
Display text shown to users |
hint |
D |
Additional help text |
guidance_hint |
E |
Extended guidance information |
appearance |
F |
Visual styling options |
required |
G |
Whether question is mandatory (yes/no) |
required_message |
H |
Custom message when required field is empty |
readonly |
I |
Whether field is read-only (yes/no) |
default |
J |
Default value for the field |
calculation |
K |
Formula for calculated fields |
constraint |
L |
Validation rules |
constraint_message |
M |
Message shown when constraint fails |
relevant |
N |
Logic for when question appears |
choice_filter |
O |
Filter choices dynamically |
repeat_count |
P |
Number of repetitions for repeat groups |
media::audio |
Q |
Audio file reference |
media::image |
R |
Image file reference |
bind::type |
S |
Data type binding |
bind::esri:fieldType |
T |
Esri field type |
bind::esri:fieldLength |
U |
Maximum field length |
bind::esri:fieldAlias |
V |
Display alias for field |
body::esri:style |
W |
Esri styling options |
bind::esri:parameters |
X |
Additional Esri parameters |
parameters |
Y |
General parameters |
body::accept |
Z |
Accepted file types |
body::esri:visible |
AA |
Visibility settings |
body::esri:inputMask |
AB |
Input formatting mask |
Choices Sheet Columns
Each choice (marked with “-”) in the choices section maps to these Excel columns:
YAML Field |
Excel Column |
Description |
|---|---|---|
list_name |
A |
Name of the choice list |
name |
B |
Internal value for the choice |
label |
C |
Display text for the choice |
media::audio |
D |
Audio file for the choice |
media::image |
E |
Image file for the choice |
Settings Sheet Columns
Settings fields map to these Excel columns:
YAML Field |
Excel Column |
Description |
|---|---|---|
form_title |
A |
Title of the form |
form_id |
B |
Unique form identifier |
instance_name |
C |
Instance naming pattern |
submission_url |
D |
URL for form submissions |
default_language |
E |
Default language code |
version |
F |
Form version number |
style |
G |
Form styling options |
namespaces |
H |
XML namespaces |
YAML Structure Examples
Basic Survey Structure
settings:
form_title: "My Survey"
instance_name: "survey_${q1}"
choices:
- list_name: yes_no
name: yes
label: "Yes"
- list_name: yes_no
name: no
label: "No"
survey:
- type: text
name: q1
label: "What is your name?"
required: yes
- type: select_one yes_no
name: q2
label: "Do you agree?"
Question Types
Text Questions:
- type: text
name: name_field
label: "Enter your name"
required: yes
hint: "First and last name"
Integer Questions:
- type: integer
name: age_field
label: "Enter your age"
constraint: ". > 0 and . < 120"
constraint_message: "Age must be between 1 and 119"
Select Questions:
- type: select_one colors
name: favorite_color
label: "What is your favorite color?"
appearance: "minimal"
Calculated Fields:
- type: text
name: full_name
label: "Full Name"
calculation: "concat(${first_name}, ' ', ${last_name})"
readonly: yes
Note Fields:
- type: note
name: instructions
label: "Please answer all questions carefully"
Groups and Repeats
Groups (questions grouped together):
- type: group
name: personal_info
label: "Personal Information"
children:
- type: text
name: first_name
label: "First Name"
- type: text
name: last_name
label: "Last Name"
Repeats (repeating sections):
- type: repeat
name: family_members
label: "Family Members"
children:
- type: text
name: member_name
label: "Member Name"
- type: integer
name: member_age
label: "Member Age"
Choice Lists
Simple Yes/No List:
choices:
- list_name: yes_no
name: yes
label: "Yes"
- list_name: yes_no
name: no
label: "No"
Multiple Choice List:
choices:
- list_name: colors
name: red
label: "Red"
- list_name: colors
name: blue
label: "Blue"
- list_name: colors
name: green
label: "Green"
Choices with Images:
choices:
- list_name: animals
name: cat
label: "Cat"
media::image: "cat.jpg"
- list_name: animals
name: dog
label: "Dog"
media::image: "dog.jpg"
Advanced Features
Variable Substitution
Use ${variable_name} syntax to reference other fields:
- type: text
name: first_name
label: "First Name"
- type: note
name: greeting
label: "Hello ${first_name}!"
Conditional Logic
Use the relevant field to show/hide questions:
- type: select_one yes_no
name: has_children
label: "Do you have children?"
- type: integer
name: num_children
label: "How many children?"
relevant: "${has_children} = 'yes'"
Validation Rules
Use constraint for validation:
- type: integer
name: age
label: "Age"
constraint: ". >= 18"
constraint_message: "You must be at least 18 years old"
Default Values
Set default values for fields:
- type: text
name: country
label: "Country"
default: "USA"
Appearance Options
Control how questions appear:
- type: select_one colors
name: color
label: "Choose color"
appearance: "minimal" # dropdown instead of radio buttons
Media Integration
Add audio or image prompts:
- type: text
name: description
label: "Describe what you see"
media::image: "photo.jpg"
media::audio: "instructions.mp3"
Testing and Preview
Preview Input Fields
Add test data using the survey123py::preview_input field:
- type: text
name: name
label: "Your name"
survey123py::preview_input: "John Doe"
This allows you to test variable substitution and preview your form with sample data.
Common Patterns
Required Fields
- type: text
name: email
label: "Email Address"
required: yes
required_message: "Email is required"
Read-only Calculated Fields
- type: text
name: timestamp
label: "Submission Time"
calculation: "now()"
readonly: yes
Conditional Sections
- type: select_one yes_no
name: need_help
label: "Do you need assistance?"
- type: group
name: help_section
label: "Help Information"
relevant: "${need_help} = 'yes'"
children:
- type: text
name: help_type
label: "What kind of help do you need?"
Best Practices
Use descriptive names: Field names should be clear and consistent
Group related questions: Use groups to organize related questions
Add hints: Provide helpful hints for complex questions
Validate input: Use constraints to ensure data quality
Test thoroughly: Use preview inputs to test your form logic
Keep it simple: Don’t overcomplicate the structure
Common Errors to Avoid
Duplicate names: Each field must have a unique name
Invalid choice references: Ensure choice lists exist before referencing them
Circular references: Don’t create loops in variable substitutions
Missing required fields: Include all mandatory fields for your question types
Incorrect boolean values: Use “yes”/”no” strings, not true/false
Example Complete Survey
settings:
form_title: "Customer Feedback Survey"
instance_name: "feedback_${customer_name}_${today()}"
namespaces: "esri=https://esri.com/xforms"
choices:
- list_name: satisfaction
name: very_satisfied
label: "Very Satisfied"
- list_name: satisfaction
name: satisfied
label: "Satisfied"
- list_name: satisfaction
name: neutral
label: "Neutral"
- list_name: satisfaction
name: dissatisfied
label: "Dissatisfied"
- list_name: satisfaction
name: very_dissatisfied
label: "Very Dissatisfied"
- list_name: yes_no
name: yes
label: "Yes"
- list_name: yes_no
name: no
label: "No"
survey:
- type: text
name: customer_name
label: "Customer Name"
required: yes
survey123py::preview_input: "John Smith"
- type: select_one satisfaction
name: overall_satisfaction
label: "How satisfied are you with our service?"
required: yes
- type: select_one yes_no
name: would_recommend
label: "Would you recommend us to others?"
required: yes
- type: text
name: improvement_suggestions
label: "What could we improve?"
relevant: "${overall_satisfaction} = 'dissatisfied' or ${overall_satisfaction} = 'very_dissatisfied'"
- type: group
name: contact_info
label: "Contact Information (Optional)"
children:
- type: text
name: email
label: "Email Address"
hint: "We'll only use this for follow-up if needed"
- type: text
name: phone
label: "Phone Number"
- type: text
name: submission_id
label: "Submission ID"
calculation: "concat('FB_', ${customer_name}, '_', format-date(today(), '%Y%m%d'))"
readonly: yes
This creates a complete customer feedback survey with conditional logic, validation, and automatic ID generation.