Skip to main content
Nintex Community Menu Bar

Working with Nintex for O365 Repeating Section Data

  • January 30, 2026
  • 0 replies
  • 7 views

When working with repeating section data from O365 and migrating to NW, there are some best practices and considerations that should be taken. 

The Challenge 

  • Repeating Sections in Nintex Forms for Office 365 store their values in XML, not in normal SharePoint columns. 
  • Unless a Repeating Section is bound to a multi-line text field, its content is hidden inside a special internal field (NFFormData/FormData). 
  • This makes the data hard to report on or export directly. 

Why This Matters 

  • Customers often need historical access to repeating section data (for audits, reports, or migration). 
  • Without action, this data remains locked in XML and is only visible inside the form. 

Options to Access the Data 

  1. Best Practice for New Forms 
    • Connect the Repeating Section to a Plain Text multi-line column
    • This ensures all future entries are stored in a standard SharePoint column. 
  2. For Existing Data 
    • Use a workflow (Query XML action) to parse Form Data. 
    • Or, use PowerShell (PnP.PowerShell) to pull repeating section XML from the hidden NFFormData field. 
    • Extracted data can be saved into a new multi-line text column for easier access. 

 

Recommended Solution for Historical Data 

  • Run a PowerShell script that: 
    1. Connects to the SharePoint list. 
    2. Reads each item’s NFFormData value. 
    3. Extracts the Repeating Section data. 
    4. Writes that data into a new multi-line text column (e.g., “RepeatingSectionArchive”). 
       
  • This approach ensures: 
    ✅ Historical data is preserved in a visible column. 
    ✅ Reports, exports, and audits can use standard list fields. 
    ✅ Future form submissions can continue writing directly to the archive column (if connected). 

Benefits to You 

  • Transparency: All Repeating Section content is visible in SharePoint
  • Control: You can filter, sort, and report on this data like any other field. 
  • Futureproofing: Once set up, no more hidden XML headaches. 

🔑 Key Takeaway: 

  • For new forms → connect Repeating Sections to a multi-line text column. 
  • For existing forms → run a PowerShell script to backfill and unlock historical data. 

  

Plain Text 

# 1) Install PnP if you don’t have it 
Install-Module PnP.PowerShell -Scope CurrentUser 
 
# 2) Run the script with your settings 
.\NintexRepeaterArchive.ps1 ` 
  -SiteUrl "https://contoso.sharepoint.com/sites/YourSite" ` 
  -ListTitle "Requests" ` 
  -TargetXmlField "RepeaterArchive" ` 
  -RepeaterXPaths "//RepeatingSection/Item" ` 
  -AlsoStoreJson $true ` 
  -TargetJsonField "RepeaterArchiveJson" 
  

Parameters you’ll set 

-SiteUrl – your SPO site URL. 

-ListTitle – the list with the Nintex items. 

-TargetXmlField – internal name for the Plain Text multi-line column to store the XML 

 

Full Script and details:

Plain Text 

<# 
.SYNOPSIS 
  Backfill Nintex for O365 Repeating Section data into SharePoint columns. 
 
.DESCRIPTION 
  Reads unbound Nintex form XML from the hidden NFFormData/FormData field for each list item, 
  extracts one or more Repeating Section blocks by XPath, and writes results to multi-line 
  text fields (XML and/or JSON) on the same item. 
 
.REQUIREMENTS 
  - PnP.PowerShell module (Install-Module PnP.PowerShell -Scope CurrentUser) 
  - Permission to read and update items on the target list 
 
.EXAMPLE 
  .\NintexRepeaterArchive.ps1 ` 
   -SiteUrl "https://contoso.sharepoint.com/sites/Finance" ` 
   -ListTitle "Expense Requests" ` 
   -TargetXmlField "RepeaterArchive" ` 
   -RepeaterXPaths "//RepeatingSection/Item" ` 
   -AlsoStoreJson $true ` 
   -TargetJsonField "RepeaterArchiveJson" 
#> 
 
param( 
  [Parameter(Mandatory=$true)][string]$SiteUrl, 
  [Parameter(Mandatory=$true)][string]$ListTitle, 
 
  # Destination (must be Note/Plain Text). Created automatically if missing. 
  [Parameter(Mandatory=$true)][string]$TargetXmlField, 
 
  # One or more XPath expressions that locate the <Item> nodes inside each Repeating Section. 
  # Example: "//RepeatingSection/Item", or "//MyRepeater/Item" 
  [Parameter(Mandatory=$true)][string[]]$RepeaterXPaths, 
 
  # Optionally also store a flattened JSON array of the rows. 
  [bool]$AlsoStoreJson = $false, 
  [string]$TargetJsonField = "RepeaterArchiveJson", 
 
  # Optional: CAML query to limit which items are processed (e.g., only items whose archive field is empty). 
  [string]$CamlQuery = $null, 
 
  # Performance knobs 
  [int]$PageSize = 500, 
  [int]$SleepMsBetweenItems = 0, 
 
  # Safety 
  [switch]$WhatIf 

 
function Ensure-PlainTextNoteField { 
  param([string]$ListTitle,[string]$InternalName) 
  $f = Get-PnPField -List $ListTitle -Identity $InternalName -ErrorAction SilentlyContinue 
  if (-not $f) { 
   Write-Host "Creating multi-line text field '$InternalName' on list '$ListTitle'..." -ForegroundColor Cyan 
   Add-PnPField -List $ListTitle -DisplayName $InternalName -InternalName $InternalName -Type Note -AddToDefaultView | Out-Null 
   # enforce Plain Text 
   Set-PnPField -List $ListTitle -Identity $InternalName -Values @{ RichText = $false } | Out-Null 
  } elseif ($f.TypeAsString -ne "Note") { 
   throw "Field '$InternalName' exists but is not a Note (multi-line) field." 
  } else { 
   # ensure Plain Text 
   Set-PnPField -List $ListTitle -Identity $InternalName -Values @{ RichText = $false } | Out-Null 
  } 

 
function Get-FormXmlFromItem { 
  param($ListItem) 
  # NFFormData is preferred; fallback to FormData 
  $raw = $ListItem["NFFormData"] 
  if (-not $raw) { $raw = $ListItem["FormData"] } 
  if ([string]::IsNullOrWhiteSpace($raw)) { return $null } 
 
  Add-Type -AssemblyName System.Web 
  $decoded = [System.Web.HttpUtility]::HtmlDecode($raw) 
 
  try { 
   [xml]$xml = $decoded 
   return $xml 
  } catch { 
   throw "Item ID $($ListItem['ID']): Failed to parse form XML. $_" 
  } 

 
function Select-RepeaterRows { 
  param([xml]$FormXml,[string[]]$XPaths) 
 
  foreach ($xp in $XPaths) { 
   try { 
     $nodes = $FormXml.SelectNodes($xp) 
     if ($nodes -and $nodes.Count -gt 0) { 
       return ,$nodes  # return immediately on first match set 
     } 
   } catch { 
     Write-Verbose "XPath error for '$xp': $_" 
   } 
  } 
  return @() 

 
function Flatten-ItemNodeToHashtable { 
  param([System.Xml.XmlNode]$Node) 
  $map = @{} 
  foreach ($child in $Node.ChildNodes) { 
   if ($child.NodeType -eq [System.Xml.XmlNodeType]::Element) { 
     # If duplicate names occur, suffix with an index 
     $name = $child.Name 
     $val  = $child.InnerText 
     if ($map.ContainsKey($name)) { 
       $i = 2 
       while ($map.ContainsKey("$name`_$i")) { $i++ } 
       $name = "$name`_$i" 
     } 
     $map[$name] = $val 
   } 
  } 
  return $map 

 
# --- Connect --- 
Connect-PnPOnline -Url $SiteUrl -Interactive 
 
# --- Ensure destination fields --- 
Ensure-PlainTextNoteField -ListTitle $ListTitle -InternalName $TargetXmlField 
if ($AlsoStoreJson) { Ensure-PlainTextNoteField -ListTitle $ListTitle -InternalName $TargetJsonField } 
 
# --- Pull items --- 
$fieldsToLoad = @("ID","NFFormData","FormData",$TargetXmlField) 
if ($AlsoStoreJson) { $fieldsToLoad += $TargetJsonField } 
 
if ($CamlQuery) { 
  $items = Get-PnPListItem -List $ListTitle -Query $CamlQuery -PageSize $PageSize -Fields $fieldsToLoad 
} else { 
  $items = Get-PnPListItem -List $ListTitle -PageSize $PageSize -Fields $fieldsToLoad 

 
$updated = 0; $skippedNoFormData = 0; $skippedNoRows = 0; $unchanged = 0; $errors = 0 
 
foreach ($it in $items) { 
  try { 
   $id = $it["ID"] 
   $formXml = Get-FormXmlFromItem -ListItem $it 
   if (-not $formXml) { $skippedNoFormData++; continue } 
 
   $rows = Select-RepeaterRows -FormXml $formXml -XPaths $RepeaterXPaths 
   if (-not $rows -or $rows.Count -eq 0) { $skippedNoRows++; continue } 
 
   # Compose outputs 
   $xmlOut = ($rows | ForEach-Object { $_.OuterXml }) -join "`r`n" 
   $vals = @{}; $changed = $false 
 
   if ([string]::IsNullOrWhiteSpace($it[$TargetXmlField]) -or $it[$TargetXmlField] -ne $xmlOut) { 
     $vals[$TargetXmlField] = $xmlOut 
     $changed = $true 
   } 
 
   if ($AlsoStoreJson) { 
     $jsonObjs = foreach ($r in $rows) { [pscustomobject](Flatten-ItemNodeToHashtable -Node $r) } 
     $jsonOut = $jsonObjs | ConvertTo-Json -Depth 10 
     if ([string]::IsNullOrWhiteSpace($it[$TargetJsonField]) -or $it[$TargetJsonField] -ne $jsonOut) { 
       $vals[$TargetJsonField] = $jsonOut 
       $changed = $true 
     } 
   } 
 
   if ($changed) { 
     if ($WhatIf) { 
       Write-Host "[WhatIf] Would update item $id" -ForegroundColor Yellow 
     } else { 
       Set-PnPListItem -List $ListTitle -Identity $id -Values $vals | Out-Null 
       Write-Host "Updated item $id" -ForegroundColor Green 
     } 
     $updated++ 
   } else { 
     $unchanged++ 
   } 
 
   if ($SleepMsBetweenItems -gt 0) { Start-Sleep -Milliseconds $SleepMsBetweenItems } 
  } 
  catch { 
   Write-Warning "Item $($it['ID']) error: $_" 
   $errors++ 
  } 

 
Write-Host "Done." -ForegroundColor Cyan 
Write-Host ("Updated: {0}  Unchanged: {1}  Skipped (no form data): {2}  Skipped (no rows): {3}  Errors: {4}" -f ` 
  $updated, $unchanged, $skippedNoFormData, $skippedNoRows, $errors) 
  

This is a self-contained, production-ready PnP.PowerShell script that backfills Nintex O365 Repeating Section data from the hidden NFFormData/FormData into multi-line text columns (XML and optional JSON). Copy it into NintexRepeaterArchive.ps1 and run with the example above. 

  

Notes

  • Make sure your destination fields are Plain Text (the script enforces this). 
  • If your form uses named sections, adjust -RepeaterXPaths (e.g., //MyRepeater/Item).