2019-09-04 03:18:21 -07:00
import { Builder , Parser } from 'xml2js' ;
import { IExecuteFunctions } from 'n8n-core' ;
import {
2020-10-01 05:01:39 -07:00
IDataObject ,
2019-09-04 03:18:21 -07:00
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
2021-04-16 09:33:36 -07:00
NodeOperationError ,
2019-09-04 03:18:21 -07:00
} from 'n8n-workflow' ;
export class Xml implements INodeType {
description : INodeTypeDescription = {
displayName : 'XML' ,
name : 'xml' ,
icon : 'fa:file-code' ,
group : [ 'transform' ] ,
version : 1 ,
subtitle : '={{$parameter["mode"]==="jsonToxml" ? "JSON to XML" : "XML to JSON"}}' ,
description : 'Convert data from and to XML' ,
defaults : {
name : 'XML' ,
color : '#333377' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
properties : [
{
displayName : 'Mode' ,
name : 'mode' ,
type : 'options' ,
options : [
{
name : 'JSON to XML' ,
value : 'jsonToxml' ,
2022-05-06 14:01:25 -07:00
description : 'Converts data from JSON to XML' ,
2019-09-04 03:18:21 -07:00
} ,
{
name : 'XML to JSON' ,
value : 'xmlToJson' ,
description : 'Converts data from XML to JSON' ,
} ,
] ,
default : 'xmlToJson' ,
2022-05-06 14:01:25 -07:00
description : 'From and to what format the data should be converted' ,
2019-09-04 03:18:21 -07:00
} ,
// ----------------------------------
// option:jsonToxml
// ----------------------------------
{
displayName : 'Property Name' ,
name : 'dataPropertyName' ,
type : 'string' ,
displayOptions : {
show : {
mode : [
'jsonToxml' ,
] ,
} ,
} ,
default : 'data' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'Name of the property to which to contains the converted XML data' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
displayOptions : {
show : {
mode : [
'jsonToxml' ,
] ,
} ,
} ,
default : { } ,
options : [
{
displayName : 'Allow Surrogate Chars' ,
name : 'allowSurrogateChars' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to allow using characters from the Unicode surrogate blocks' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Attribute Key' ,
name : 'attrkey' ,
type : 'string' ,
default : '$' ,
2022-05-06 14:01:25 -07:00
description : 'Prefix that is used to access the attributes' ,
2019-09-04 03:18:21 -07:00
} ,
2020-02-11 08:03:24 -08:00
{
2022-06-03 10:23:49 -07:00
displayName : 'Cdata' ,
2020-02-11 08:03:24 -08:00
name : 'cdata' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to wrap text nodes in <![CDATA[ ... ]]> instead of escaping when necessary. Does not add <![CDATA[ ... ]]> if it is not required.' ,
2020-02-11 08:03:24 -08:00
} ,
2019-09-04 03:18:21 -07:00
{
displayName : 'Character Key' ,
name : 'charkey' ,
type : 'string' ,
default : '_' ,
2022-05-06 14:01:25 -07:00
description : 'Prefix that is used to access the character content' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Headless' ,
name : 'headless' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to omit the XML header' ,
2019-09-04 03:18:21 -07:00
} ,
2020-02-11 08:03:24 -08:00
{
displayName : 'Root Name' ,
name : 'rootName' ,
type : 'string' ,
default : 'root' ,
2022-05-06 14:01:25 -07:00
description : 'Root element name to be used' ,
2020-02-11 08:03:24 -08:00
} ,
2019-09-04 03:18:21 -07:00
] ,
} ,
// ----------------------------------
// option:xmlToJson
// ----------------------------------
{
displayName : 'Property Name' ,
name : 'dataPropertyName' ,
type : 'string' ,
displayOptions : {
show : {
mode : [
'xmlToJson' ,
] ,
} ,
} ,
default : 'data' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'Name of the property which contains the XML data to convert' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
displayOptions : {
show : {
mode : [
'xmlToJson' ,
] ,
} ,
} ,
default : { } ,
options : [
{
displayName : 'Attribute Key' ,
name : 'attrkey' ,
type : 'string' ,
default : '$' ,
2022-05-06 14:01:25 -07:00
description : 'Prefix that is used to access the attributes' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Character Key' ,
name : 'charkey' ,
type : 'string' ,
default : '_' ,
2022-05-06 14:01:25 -07:00
description : 'Prefix that is used to access the character content' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Explicit Array' ,
name : 'explicitArray' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to always put child nodes in an array if true; otherwise an array is created only if there is more than one' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Explicit Root' ,
name : 'explicitRoot' ,
type : 'boolean' ,
default : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether to set this if you want to get the root node in the resulting object' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Ignore Attributes' ,
name : 'ignoreAttrs' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to ignore all XML attributes and only create text nodes' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Merge Attributes' ,
name : 'mergeAttrs' ,
type : 'boolean' ,
default : true ,
2022-06-20 07:54:01 -07:00
description : 'Whether to merge attributes and child elements as properties of the parent, instead of keying attributes off a child attribute object. This option is ignored if ignoreAttrs is true.' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Normalize' ,
name : 'normalize' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to trim whitespaces inside text nodes' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Normalize Tags' ,
name : 'normalizeTags' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to normalize all tag names to lowercase' ,
2019-09-04 03:18:21 -07:00
} ,
{
displayName : 'Trim' ,
name : 'trim' ,
type : 'boolean' ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether to trim the whitespace at the beginning and end of text nodes' ,
2019-09-04 03:18:21 -07:00
} ,
] ,
} ,
2020-10-22 06:46:03 -07:00
] ,
2019-09-04 03:18:21 -07:00
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
const mode = this . getNodeParameter ( 'mode' , 0 ) as string ;
const dataPropertyName = this . getNodeParameter ( 'dataPropertyName' , 0 ) as string ;
const options = this . getNodeParameter ( 'options' , 0 , { } ) as IDataObject ;
let item : INodeExecutionData ;
2022-02-04 03:16:34 -08:00
const returnData : INodeExecutionData [ ] = [ ] ;
2019-09-04 03:18:21 -07:00
for ( let itemIndex = 0 ; itemIndex < items . length ; itemIndex ++ ) {
2021-07-19 23:58:54 -07:00
try {
2019-09-04 03:18:21 -07:00
2021-07-19 23:58:54 -07:00
item = items [ itemIndex ] ;
2019-09-04 03:18:21 -07:00
2021-07-19 23:58:54 -07:00
if ( mode === 'xmlToJson' ) {
const parserOptions = Object . assign ( {
mergeAttrs : true ,
explicitArray : false ,
} , options ) ;
2019-09-04 03:18:21 -07:00
2021-07-19 23:58:54 -07:00
const parser = new Parser ( parserOptions ) ;
2019-09-04 03:18:21 -07:00
2021-07-19 23:58:54 -07:00
if ( item . json [ dataPropertyName ] === undefined ) {
2022-07-12 08:51:01 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` No json property " ${ dataPropertyName } " does not exists on item! ` , { itemIndex } ) ;
2021-07-19 23:58:54 -07:00
}
2019-09-04 03:18:21 -07:00
2021-07-19 23:58:54 -07:00
// @ts-ignore
const json = await parser . parseStringPromise ( item . json [ dataPropertyName ] ) ;
2022-02-04 03:16:34 -08:00
returnData . push ( { json } ) ;
2021-07-19 23:58:54 -07:00
} else if ( mode === 'jsonToxml' ) {
const builder = new Builder ( options ) ;
2022-02-04 03:16:34 -08:00
returnData . push ( {
2021-07-19 23:58:54 -07:00
json : {
[ dataPropertyName ] : builder . buildObject ( items [ itemIndex ] . json ) ,
} ,
2022-06-03 08:25:07 -07:00
pairedItem : {
item : itemIndex ,
} ,
2022-02-04 03:16:34 -08:00
} ) ;
2021-07-19 23:58:54 -07:00
} else {
2022-07-12 08:51:01 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` The operation " ${ mode } " is not known! ` , { itemIndex } ) ;
2021-07-19 23:58:54 -07:00
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
2022-06-03 08:25:07 -07:00
items [ itemIndex ] = ( {
json : {
error : error.message ,
} ,
pairedItem : {
item : itemIndex ,
} ,
} ) ;
2021-07-19 23:58:54 -07:00
continue ;
}
throw error ;
2019-09-04 03:18:21 -07:00
}
}
2022-02-04 03:16:34 -08:00
return this . prepareOutputData ( returnData ) ;
2019-09-04 03:18:21 -07:00
}
}