Processing XML Import via Script?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-18-2023 01:18 PM
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
10-18-2023 03:42 PM
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.
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-12-2025 12:06 AM - edited 06-12-2025 12:09 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
After doing this, how would you commit this update set, through the API?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 weeks ago
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"}))
