Skip to content

Instantly share code, notes, and snippets.

@trulysinclair
Last active July 10, 2024 22:45
Show Gist options
  • Save trulysinclair/5dc5832cee4d4e47299c376a7e1cd2b7 to your computer and use it in GitHub Desktop.
Save trulysinclair/5dc5832cee4d4e47299c376a7e1cd2b7 to your computer and use it in GitHub Desktop.
Convert xml2js output into badgerfish convention
import { toBadgerfish } from '#util/badgerfish/to_badgerfish'
import { JsonObject } from '#util/badgerfish/types'
import { Parser } from 'xml2js'
/**
* Convert XML to Badgerfish JSON convention
* @param {string} xml
* @returns {Promise<JsonObject>}
*/
export async function fromXml(xml: string) {
const node: JsonObject = await new Parser({
explicitArray: false,
trim: true,
normalize: true,
preserveChildrenOrder: true,
tagNameProcessors: [
(tagName) => {
if (tagName.startsWith('xsi:')) {
return tagName.replace('xsi:', '')
}
return tagName
},
],
}).parseStringPromise(xml)
return toBadgerfish(node)
}
import { isValidChildProperty } from '#util/badgerfish/is_valid_child_property'
import { JsonObject } from '#util/badgerfish/types'
export function hasChildProperties(node: JsonObject): boolean {
if (typeof node !== 'object' || node === null) {
return false
}
const typedNode = node as JsonObject
for (let key in typedNode) {
if (isValidChildProperty(typedNode, key)) {
return true
}
}
return false
}
import { JsonObject } from '#util/badgerfish/types'
export function isValidChildProperty(node: JsonObject, key: string): boolean {
return node.hasOwnProperty(key) && typeof node[key] === 'object' && node[key] !== null
}
import { JsonObject } from '#util/badgerfish/types'
// TODO redo this in stages. i.e. handle namespaces, then values, then arrays, etc.
export function toBadgerfish(node: JsonObject) {
if (typeof node !== 'object' || node === null) return node
let badgerfishNode: JsonObject = {}
for (let key in node) {
if (node.hasOwnProperty(key)) {
// handle array
if (Array.isArray(node[key])) {
badgerfishNode[key] = (node[key] as Array<JsonObject>).map((i) => {
if (typeof i !== 'object') {
return {
$: toBadgerfish(i),
}
} else return toBadgerfish(i)
})
}
// handle child object
else if (typeof node[key] === 'object') {
// handle attributes
if (key === '$') {
const namespace = {}
// go through each attribute, replacing the object for independent
// attributes prefixed with the "@" character
for (const attribute in node[key] as JsonObject) {
if (!attribute.includes('xmlns')) {
// @ts-ignore
badgerfishNode[`@${attribute}`] = node[key][attribute]
} else {
// append default namespace
if (attribute === 'xmlns') {
Object.assign(namespace, {
// @ts-ignore
$: node[key][attribute],
})
}
// append named namespaces
else
Object.assign(namespace, {
// @ts-ignore
[attribute.replace('xmlns:', '')]: node[key][attribute],
})
}
}
if (!Object.entries(namespace).length) {
// noop
} else badgerfishNode['@xmlns'] = namespace
}
// handle child objects
else badgerfishNode[key] = toBadgerfish(node[key] as JsonObject)
}
// Xml2Js puts text under "_", so put it in "$"
else if (key === '_') {
badgerfishNode['$'] = node[key]
}
// set values in "$"
else {
// handle text or attribute
badgerfishNode[key] = { $: node[key] }
}
}
}
return badgerfishNode
}
export type JsonValue = string | number | boolean | JsonObject | JsonObject[]
export interface JsonObject {
[key: string]: JsonValue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment