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' ,
description : 'Converts data from JSON to XML.' ,
} ,
{
name : 'XML to JSON' ,
value : 'xmlToJson' ,
description : 'Converts data from XML to JSON' ,
} ,
] ,
default : 'xmlToJson' ,
description : 'From and to what format the data should be converted.' ,
} ,
// ----------------------------------
// option:jsonToxml
// ----------------------------------
{
displayName : 'Property Name' ,
name : 'dataPropertyName' ,
type : 'string' ,
displayOptions : {
show : {
mode : [
'jsonToxml' ,
] ,
} ,
} ,
default : 'data' ,
required : true ,
2021-10-27 13:00:13 -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 ,
description : 'Allows using characters from the Unicode surrogate blocks.' ,
} ,
{
displayName : 'Attribute Key' ,
name : 'attrkey' ,
type : 'string' ,
default : '$' ,
description : 'Prefix that is used to access the attributes.' ,
} ,
2020-02-11 08:03:24 -08:00
{
displayName : 'cdata' ,
name : 'cdata' ,
type : 'boolean' ,
default : false ,
description : ' wrap text nodes in <![CDATA[ ... ]]> instead of escaping when necessary. Does not add <![CDATA[ ... ]]> if it is not required.' ,
} ,
2019-09-04 03:18:21 -07:00
{
displayName : 'Character Key' ,
name : 'charkey' ,
type : 'string' ,
default : '_' ,
description : 'Prefix that is used to access the character content.' ,
} ,
{
displayName : 'Headless' ,
name : 'headless' ,
type : 'boolean' ,
default : false ,
description : 'Omit the XML header.' ,
} ,
2020-02-11 08:03:24 -08:00
{
displayName : 'Root Name' ,
name : 'rootName' ,
type : 'string' ,
default : 'root' ,
description : 'Root element name to be used.' ,
} ,
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 ,
2021-10-27 13:00:13 -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 : '$' ,
description : 'Prefix that is used to access the attributes.' ,
} ,
{
displayName : 'Character Key' ,
name : 'charkey' ,
type : 'string' ,
default : '_' ,
description : 'Prefix that is used to access the character content.' ,
} ,
{
displayName : 'Explicit Array' ,
name : 'explicitArray' ,
type : 'boolean' ,
default : false ,
description : 'Always put child nodes in an array if true; otherwise an array is created only if there is more than one.' ,
} ,
{
displayName : 'Explicit Root' ,
name : 'explicitRoot' ,
type : 'boolean' ,
default : true ,
description : 'Set this if you want to get the root node in the resulting object.' ,
} ,
{
displayName : 'Ignore Attributes' ,
name : 'ignoreAttrs' ,
type : 'boolean' ,
default : false ,
description : 'Ignore all XML attributes and only create text nodes.' ,
} ,
{
displayName : 'Merge Attributes' ,
name : 'mergeAttrs' ,
type : 'boolean' ,
default : true ,
description : '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.' ,
} ,
{
displayName : 'Normalize' ,
name : 'normalize' ,
type : 'boolean' ,
default : false ,
description : 'Trim whitespaces inside text nodes.' ,
} ,
{
displayName : 'Normalize Tags' ,
name : 'normalizeTags' ,
type : 'boolean' ,
default : false ,
description : 'Normalize all tag names to lowercase.' ,
} ,
{
displayName : 'Trim' ,
name : 'trim' ,
type : 'boolean' ,
default : false ,
description : 'Trim the whitespace at the beginning and end of text nodes.' ,
} ,
] ,
} ,
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 ;
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 ) {
throw new NodeOperationError ( this . getNode ( ) , ` No json property " ${ dataPropertyName } " does not exists on item! ` ) ;
}
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 ] ) ;
items [ itemIndex ] = { json } ;
} else if ( mode === 'jsonToxml' ) {
const builder = new Builder ( options ) ;
items [ itemIndex ] = {
json : {
[ dataPropertyName ] : builder . buildObject ( items [ itemIndex ] . json ) ,
} ,
} ;
} else {
throw new NodeOperationError ( this . getNode ( ) , ` The operation " ${ mode } " is not known! ` ) ;
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
items [ itemIndex ] = ( { json : { error : error.message } } ) ;
continue ;
}
throw error ;
2019-09-04 03:18:21 -07:00
}
}
return this . prepareOutputData ( items ) ;
}
}