Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

Processing XML Import via Script?

Gajendra Singh1
ServiceNow Employee
ServiceNow Employee

Is there a way to process an XML import file via script rather than GUI? Instead of going to "Import XML" have a script that pulls the file by location or content and processes it? 

4 REPLIES 4

ChrisBurks
Giga Sage

Depending upon what is needed the basic way is to use a data source.

Setting the "Type" field as "File" or "Custom script".

  • With File type the system will parse the data based on the "Format" field and "XPath for each row" field.
    • "Format" field set to "XML"
    • "XPath for each row" field set with the xpath to indicate what are the list of records. For example if you had an unload XML of a list of exported incidents the XPath might be something like "/unload/incident"
  • With the Custom script type you would write a script to do the parsing and fetching

There is also a field called "File retrieval method" which indicates how the file gets pulled which can be from an attachment (attached to the data source record) or various protocol methods like FTP, SFTP, HTTPS etc.

 

This would work great for updating existing records.

 

The tricky part is if you're also expecting to import new records (such as moving data from instance to another instance). There will be some extra work for system fields such as keeping the "sys_created_on" dates and things of the a nature as well as comments and they're original created and user info.

 

https://docs.servicenow.com/bundle/vancouver-integrate-applications/page/administer/import-sets/task...data_source_xml_unload_example.png

 

Another possibility is to use a combination of the data source with Type Custom script and a Table REST API.

In the Data Source's  Custom script parse the through the XML (leverage the XMLDocumentV2 API) and then point to the Table REST API to either update/create records based on the data.

 

XMLDocumentV2: https://developer.servicenow.com/dev.do#!/reference/api/utah/server/no-namespace/c_XMLDocument2Scope...

 

Table API: https://developer.servicenow.com/dev.do#!/reference/api/utah/rest/c_TableAPI

 

Kannan Mani
Tera Contributor

Yes, you can make an HTTP POST request to the sys_upload.do endpoint using an admin-level API account. Be sure to include the necessary HTTP request parameters, which you can observe in the network activity log when manually uploading the XML file through the UI.

Make sure to include HTTP session data along with the auth-creds in the post request. 

After doing this, how would you commit this update set, through the API?

Actually I am unable to make any POST request that uploads the XML. I tried many variations of the scripts similar to what I am seeing in the network log. 

 

This is the script I used, any suggestions? 

 

import os 
import sys 
import json
import re 
from urllib.parse import quote
sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))

from src.browsergym.workarena.instance import SNowInstance
from src.browsergym.workarena.api.utils import table_api_call 
import requests 
from pathlib import Path
from dotenv import load_dotenv
load_dotenv('../../.env', override=True)

# Import auth credentials from environment variables

username = os.getenv("SNOW_INSTANCE_UNAME")
password = os.getenv("SNOW_INSTANCE_PWD")
auth = (username, password)
instance = SNowInstance(snow_url=os.getenv("SNOW_INSTANCE_URL"), snow_credentials=auth)
SNOW_API_HEADERS = {"Content-Type": "application/json", "Accept": "application/json"} 
XML_PATH =  Path("../../../world-of-workflows/artifacts/ewm_world.xml")

sess = requests.Session()
sess.headers.update({"User-Agent": "Mozilla/5.0"})

base = instance.snow_url.rstrip("/")

# 1) Establish a *UI session*
r = sess.post(f"{base}/login.do",
              data={"user_name": username, "user_password": password},
              allow_redirects=True)
r.raise_for_status()

# 2) Load upload form to get cookies + CSRF + actual input name
ref_url = "sys_remote_update_set_list.do?sysparm_fixed_query=sys_class_name=sys_remote_update_set"
upload_form_url = f"{base}/upload.do?sysparm_target=sys_remote_update_set&sysparm_referring_url={quote(ref_url, safe='')}"
form = sess.get(upload_form_url, headers={"Accept": "text/html"}, allow_redirects=True)
form.raise_for_status()
html = form.text

# CSRF
ck = None
m = re.search(r'name="sysparm_ck"\s+value="([^"]+)"', html)
if m: ck = m.group(1)
if not ck:
    m = re.search(r"g_ck\s*=\s*'([^']+)'", html) or re.search(r'var\s+g_ck\s*=\s*"([^"]+)"', html)
    if m: ck = m.group(1)
if not ck:
    raise RuntimeError("Could not find CSRF token (sysparm_ck/g_ck) in upload.do response")
print("CK:", ck[:12], "…")

# Actual file input name (fallback to attachFile)
m = re.search(r'<input[^>]+type=["\']file["\'][^>]*name=["\']([^"\']+)["\']', html, flags=re.I)
file_field = m.group(1) if m else "attachFile"
print("File field name:", file_field)

# 3) Post to sys_upload.do (NO redirects) and include X-UserToken
with XML_PATH.open("rb") as f:
    files = {file_field: (XML_PATH.name, f, "application/xml")}
    data = {
        "sysparm_target": "sys_remote_update_set",
        "sysparm_referring_url": ref_url,
        "sysparm_ck": ck,
    }
    headers = {
        "Origin": base,
        "Referer": upload_form_url,
        "Accept": "text/html,application/xhtml+xml",
        "X-UserToken": ck,  # some instances require this header in addition to the form field
    }
    up = sess.post(f"{base}/sys_upload.do", data=data, files=files, headers=headers, allow_redirects=False)

print("Upload status:", up.status_code)
print("Upload Location:", up.headers.get("Location"))

# If there’s a redirect with a message page, fetch it once to see errors
if up.is_redirect and up.headers.get("Location"):
    loc = up.headers["Location"]
    if not loc.startswith("http"):
        loc = base + ("/" if not up.headers["Location"].startswith("/") else "") + up.headers["Location"]
    msg = sess.get(loc, headers={"Accept": "text/html"}, allow_redirects=True)
    print("Post-redirect URL:", msg.url)
    # Sometimes the message is in 'sysparm_message' or in the page; dump a short snippet
    print("Post-redirect snippet:", msg.text[:500].replace("\n", " "))

import time 
time.sleep(5)

# 4) Verify via Table API using the same session cookies (no Basic auth needed now)
def get_json(url, params=None):
    r = sess.get(url, params=params, headers=SNOW_API_HEADERS, auth=auth)
    r.raise_for_status()
    return r.json()

print("\nLatest Retrieved Update Set:")
print(get_json(f"{base}/api/now/table/sys_remote_update_set",
               params={"sysparm_query": "ORDERBYDESCsys_created_on", "sysparm_limit": "3"}))

print("\nLatest sys_update_xml rows (payloads inside an update set):")
print(get_json(f"{base}/api/now/table/sys_update_xml",
               params={"sysparm_query": "ORDERBYDESCsys_created_on", "sysparm_limit": "5"}))