This page is a reference guide for users who edit the raw XML in ODK forms. If you use the XLSForm form designer, see for form design help.

Bindings are where you specify data types, constraints, branching, and any other form logic. An example is shown below.

<bind nodeset="/data/branch" type="select1"/>
<bind nodeset="/data/mystring" type="string" 
  relevant="selected(/widgets/branch, 'n')"/>

Below is a table of the supported keywords and operators. Our documentation may fall out of date, so if you are a developer, you can get a more updated list in the JavaRosa source code.


Keyword Usage
nodeset Specify which instance element this binding is referring to
relevant Relevancy is how to branch through a form. If a node is not relevant, it doesn't appear to the user
readonly If a node is read only, the user will only be able to view that prompt, not enter data
required A required field will not allow a blank or empty answer before proceeding
constraint Constraint allows you to specify acceptable answers for the specified prompt
type Data type
calculate Allows you to run a calculation using this and/or other answers
jr:constraintMsg The message that will be displayed if the specified constraint is violated
jr:preload JavaRosa provides preloaders that can automatically give you information about the form. These are discussed below
jr:preloadParams Parameters used by jr:preload
jr:count References a number used to determine number of repeats. Only used in the <body>


The following table has a Notes column that can be viewed by using the scrollbar at the bottom of the table.

Operator Usage Example Notes
this current prompt's answer . constraint=". < 10.51" If current answer is less than 10.51
the current prompt's enclosing group .. constraint=". < ../q1" If current answer is less than the value of the 'q1' prompt within this same group.
not not(expression) relevant="not(selected(/widgets/select1, 'c'))" As long as 'c' is not selected in /widgets/select1
and and constraint="selected(., 'c') and selected(., 'd')" Both 'c' and 'd' need to be selected in the current prompt
or or constraint="selected(., 'c') or selected(., 'd')" Either 'c' or 'd' needs to be selected in the current prompt
conditional if(condition, a, b) calculate="if(selected(/data/q1, 'yes') and selected(/data/q2, 'yes'), 'yes', 'no')" If true return a, else return b
equal to
(not equal to)
constraint=". = number('10')" Current answer must be equal to 10
greater than
(or equal)
constraint=". > 10.51" Greater than 10.51, can also be combined with equals: >=
NOTE: XLSForm and Build translate > into &gt; within the XML file (> is a reserved character in XML).
less than
(or equal)
constraint=". < 10.51" less than 10.51, can also be combined with equals: <=
NOTE: XLSForm and Build translate < into &lt; within the XML file (< is a reserved character in XML).
addition and subtraction +
constraint="(. + ../q1) < 10.51" this value plus the value of 'q1' is less than 10.51
multiplication and division *
constraint="(. div ../q1) < 10.51" this value divided by the value of 'q1' is less than 10.51
modulo (e.g., testing for even/odd values) mod constraint="(. mod 2) = 0" this value is an even number (this value modulo 2 is zero)
selected selected(xpath/to/node, value) relevant="selected(/widgets/branch, 'n')" Checks if answer in selected prompt is selected
jr:choice-name jr:choice-name(value, 'xpath/to/node') calculate="if(string-length(/widgets/yesno)!=0,jr:choice-name(/widgets/yesno,'/widgets/yesno'),'unspecified')" If /widgets/yesno has a value selected, obtain the display string for it. This will be in the currently selected display language. NOTE: the list of choices cannot use filter conditions (it cannot be a cascading-select list). The bracketing if(...) is needed due to a limitation in javarosa ( issue 921 ).
(n+1)th selected value selected-at(xpath/to/node, position) selected-at(multi-select, 3) Return the (position+1)th (e.g., 4th) selected value in a multiple-choice select.
count selected count-selected(xpath/to/value) count-selected(multi-select) Return the number of selected answers
field within n-th repeat group indexed-repeat(field,group,index, subgroup,subidx, ssgrp,ssidx,...) indexed-repeat(/widgets/names/name,/widgets/names,1) References the field of the n-th repeat of a group. Groups and indices for 1 to 3 nested repeats can be specified. For example, the XLSForm calculate expression "indexed-repeat(${name}, ${names}, 1)" will return the value of the first "name" field inside a repeat group named "names".
true true() required="true()"
false false() required="false()"
convert to boolean boolean (xpath/to/node or value) relevant="boolean(/widgets/branch)" Conversion varies depending on data type of x
boolean from string boolean-from-string(xpath/to/node) boolean-from-string (xpath/to/node) Returns true if x is "true" or "1", false otherwise. note that this is different behaviour from boolean(x)
convert to number number (xpath/to/node or value) constraint=". < number(/data/age)" Conversion varies depending on data type of x
date, dateTime, time as number decimal-date-time (xpath/to/node or value)
decimal-time (xpath/to/node or value)
constraint="decimal-date-time(.) - decimal-date-time(../q1) < 5" This date is within 5 days of 'q1' date. The unit of conversion is one day. Date and date-time values can be truncated or rounded by other logic within Javarosa; please verify these functions work as you intend before relying upon them!
convert to integer int (xpath/to/node or value) constraint=". < int(/data/age)" Conversion varies depending on data type of x
convert to string string (xpath/to/node or value) "string (/data/age)" Conversion varies depending on data type of x
date, date-time, formatted as string format-date(xpath/to/node, format)
format-date-time(xpath/to/node, format)
format-date (xpath/to/node, '%Y/%n/%e %H:%M') Returns the date value at the xpath/to/node formatted as defined by the format argument. NOTE: date-time values may be truncated or rounded by other logic within Javarosa; please verify behavior before relying upon this operation. See Format Source Code for available '%' format specifiers.
convert to date date (xpath/to/node or value) constraint=". >= date('2011-11-12')" Conversion varies depending on data type of x. Format is yyyy-mm-dd
regular expression regex(xpath/to/node, regular expression) constraint="regex(., '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}')" This particular regex checks for a valid email address. See Regex Patterns. Complex patterns may cause ODK Collect to force close due to execution stack overflow.
first non-empty value coalesce(a, b) calculate="coalesce(/data/name, /data/national_id)" If name has a value, return name, else return national_id
concatenated string values concat(xpath/to/value1, xpath/to/value2, ...) calculate="concat(/data/name, ' with id: ', /data/national_id)" Returns the concatenation of the string values.
concatenate string values with a separator string join(separatorString, nodeset)
join(separatorString, xpath/to/value1, xpath/to/value2, ...)
calculate="concat('The children of the household are: ', join(', ', /data/household/childname))" Returns the concatenation of the string values using the first argument as a separator between values.
extract a substring substr(xpath/to/value, start)
substr(xpath/to/value, start, end)
calculate="substr('Test',1,2)='e'" Returns the substring beginning at the specified start and extends to the character at index end - 1. start and end begin at '0'.
length of a string string-length(xpath/to/value) calculate="string-length('Test')=4" return the length of a non-empty string
count(nodeset) count(nodeset) count(nodeset) Count the number of nodes in nodeset
sum(nodeset) sum(nodeset) sum(nodeset) Return the sum of the values of the nodes in nodeset
max(nodeset) max(nodeset) max(nodeset) Return the maximum of the values of the nodes in nodeset
min(nodeset) min(nodeset) min(nodeset) Return the minimum of the values of the nodes in node set
round(value, power) round(xpath/to/value, power) calculate="round(/data/q1, 3)" Return the rounded value of q1, as in Excel
pow(value, power) pow(xpath/to/value, power) calculate="pow(/data/q1, 3)" Return the cube of q1 (q1 raised to the 3rd power).
today() today() today() Return today's date
now() now() now() Return a timestamp for this instant
once(expr) once(expr) once(random()*3) If the field already has a value, return the existing value, otherwise evaluate the expression to provide a value.
random() random() random() Return a random number between 0.0 (inclusive) and 1.0 (exclusive). You should wrap this in once()
uuid() uuid() uuid() Return a random UUID string
at least X of, at most X of checklist(min, max, v1, v2, v3, ..., vn) checklist(min, max, v1, v2, v3, ..., vn) V1 through vn are a set of n yes/no answers. Return TRUE if the count of 'yes' is between minimum and maximum, inclusive. Minimum or maximum may each be -1 to indicate 'not applicable'.
weighted at least X of, at most X of weighted-checklist(min, max, v1, w1, v2, w2, v3, w3, ..., vn, wn) weighted-checklist(min, max, v1, w1, v2, w2, v3, w3, ..., vn, wn) V1 through vn are a set of n yes/no answers. w1 through wn are weights of the 'yes' answers to those questions. Return TRUE if the weighted sum of 'yes' is between minimum and maximum, inclusive. Minimum or maximum may each be -1 to indicate 'not applicable'.
get property value property(name_of_property) property('deviceid') Return the deviceid property value
position(xpath) position(xpath) position(..) Return the XML position() value of this node in a filtered XPath expression. Note that position() does NOT work.


Below are common examples of bindings that may be useful. Since these show the actual XML of the bind statement, the > and < symbols are written as &gt; and &lt; (> and < are reserved characters in XML files).

Simple String

<bind nodeset="/widgets/mystring" type="string"/>

Required Integer

<bind nodeset="/widgets/int" type="int" required="true()"/>

Branching: Show this prompt if the answer to /widget/int is less than 8

<bind nodeset="/widgets/int2" type="int" relevant="/widgets/int &lt; 8"/>

Branching: Show this prompt if the answer to /widget/branch (a select1) is "n"

<!-- the value 'n' is the <value>, not the <label> in 
  the <item> in the select or select1 -->
<bind nodeset="/widgets/language" type="string" 
  relevant="selected(/widgets/branch, 'n')"/>

Constraints: Answer must be after date set in /widgets/date1

<bind nodeset="/widgets/date2" type="date"
  constraint=". > /widgets/date1"
  jr:constraintMsg="date must be after /widgets/date1"/>

Constraints: Answer must be after today

<bind nodeset="/widgets/date" type="date" 
  constraint=". > today()" 
  jr:constraintMsg="only future dates allowed"/>

Constraints: Answer must between 4 and 10 characters long (inclusive)

<bind nodeset="/widgets/string" 
   type="string" constraint="string-length(.) &gt;= 4 and string-length(.) &lt;= 10"/>

Constraints: Answer must be lowercase letters and between 1 and 10 characters long (inclusive)

<bind nodeset="/widgets/string" 
   type="string" constraint="(regex(., "^[a-z]{1,10}$"))"/>

Constraints: "c" and "d" cannot be selected at the same time

<bind nodeset="/widgets/select" type="select" 
  constraint="not(selected(., 'c') and selected(., 'd'))" 
  jr:constraintMsg="option c and d cannot be selected together"/>

Constraints: Multi-lingual constraint message

<!-- use itext to define various language text -->
<bind nodeset="/data/prompt" type="int" constraint=". &lt;= 10"

Regular Expression: Answer must be an email address like

<bind nodeset="/widgets/regex" type="string" required="true()" 
 constraint="regex(., '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}')" 
 jr:constraintMsg="this isn't a valid email address"/>

Repeat a group N times, where N is an answer to another prompt in the form

<!--  rare cases where the binding is defined in the body -->
  <label>repeat node a</label>
  <repeat nodeset="/data/repeat_node_a" jr:count="/data/repeat_count_a" 
    <input ref="/data/repeat_node_a/repeat_string_a">
      <label>this should repeat <output value="/data/repeat_count_a"/> 
        times and finish</label>

Setting a relevant condition on a repeat (or non-repeat) group

<!--  you can set relevant conditions on groups and repeats -->
        <data id="build_TestForm_1331813278">
          <add_cate jr:template="">
      <bind nodeset="/data/id" type="string" required="true()"/>
      <bind nodeset="/data/add_cate" relevant="(/data/id = '100')"/>
      <bind nodeset="/data/add_cate/name" type="string"/>
    <input ref="/data/id">
      <hint>Entering 100 will skip repeat group</hint>
      <label>Additional Categories</label>
      <repeat nodeset="/data/add_cate" appearance="field-list">
        <input ref="/data/add_cate/name">


You can add the binds below to your form for hidden or pre-loaded fields you want automatically filled in. You'll need a variable for them in the instance and a binding, but they have no body element and the user will never see or interact with them. A more complete list of the available jr:preloadParams values is available here.

Form start time

<!--  stored the first time the form is loaded -->
<bind nodeset="/widgets/start_time" type="dateTime"

Form end time

<!--  updated every time the form is saved -->
<bind nodeset="/widgets/end_time" type="dateTime"

Current date

<bind nodeset="/widgets/date_today" type="date"

Device ID (IMEI)

<bind nodeset="/widgets/deviceid" type="string"

Subscriber ID (IMSI)

<bind nodeset="/widgets/my_subscriberid" type="string" 

SIM serial number

<bind nodeset="/widgets/my_simid" type="string" 

Device phone number

<bind nodeset="/widgets/my_phonenumber" type="string"