Skip to content

Commit

Permalink
Add custom JS object parser. See #25
Browse files Browse the repository at this point in the history
  • Loading branch information
oozcitak committed Jul 17, 2020
1 parent 5ae264f commit 0632d4e
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 70 deletions.
6 changes: 3 additions & 3 deletions src/builder/XMLBuilderImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
JSONWriterOptions, ObjectWriterOptions, MapWriterOptions
} from "../interfaces"
import {
applyDefaults, isObject, isString, isMap, isArray, isEmpty, isFunction,
applyDefaults, isObject, isString, isMap, isArray, isEmpty,
getValue, forEachObject, forEachArray, isSet
} from "@oozcitak/util"
import { XMLWriter, MapWriter, ObjectWriter, JSONWriter } from "../writers"
Expand Down Expand Up @@ -58,10 +58,10 @@ export class XMLBuilderImpl implements XMLBuilder {

if (isString(p1) && /^\s*</.test(p1)) {
// parse XML document string
return new XMLReader().parse(this, p1)
return new XMLReader(this._options).parse(this, p1)
} else if (isString(p1) && /^\s*[\{\[]/.test(p1)) {
// parse JSON string
return new JSONReader().parse(this, p1)
return new JSONReader(this._options).parse(this, p1)
} else if (isObject(p1)) {
// ele(obj: ExpandObject)
return new ObjectReader(this._options).parse(this, p1)
Expand Down
96 changes: 90 additions & 6 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ export interface XMLBuilderOptions {
* - str - the input string
*/
invalidCharReplacement: string | ((char: string, offset: number, str: string) => string) | undefined
/**
* Defines custom parser functions.
*/
parser: ParserOptions | undefined
}

/**
Expand Down Expand Up @@ -131,7 +135,8 @@ export const DefaultBuilderOptions: XMLBuilderOptions = {
svg: "http://www.w3.org/2000/svg",
xlink: "http://www.w3.org/1999/xlink"
},
invalidCharReplacement: undefined
invalidCharReplacement: undefined,
parser: undefined
}

/**
Expand Down Expand Up @@ -272,6 +277,85 @@ export interface ConvertOptions {
comment: string
}

/**
* Defines custom parser functions.
*/
export type ParserOptions = {
/**
* Main parser function which parses the given object and returns an XMLBuilder.
*
* @param node - node to recieve parsed content
* @param obj - object to parse
*/
parse?: (node: XMLBuilder, obj: string | ExpandObject) => XMLBuilder

/**
* Creates a DocType node.
* The node will be skipped if the function returns `undefined`.
*
* @param name - node name
* @param publicId - public identifier
* @param systemId - system identifier
*/
docType?: (name: string, publicId: string, systemId: string) => XMLBuilder | undefined

/**
* Creates a comment node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
*/
comment?: (parent: XMLBuilder, data: string) => XMLBuilder | undefined

/**
* Creates a text node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
*/
text?: (parent: XMLBuilder, data: string) => XMLBuilder | undefined

/**
* Creates a processing instruction node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param target - instruction target
* @param data - node data
*/
instruction?: (parent: XMLBuilder, target: string, data: string) => XMLBuilder | undefined

/**
* Creates a CData section node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
*/
cdata?: (parent: XMLBuilder, data: string) => XMLBuilder | undefined

/**
* Creates an element node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param name - node name
*/
element?: (parent: XMLBuilder, name: string) => XMLBuilder | undefined

/**
* Creates an attribute or namespace declaration.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param name - node name
* @param value - node value
*/
attribute?: (parent: XMLBuilder, name: string, value: string) => XMLBuilder | undefined
}

/**
* Defines the options passed to the writer.
*/
Expand Down Expand Up @@ -544,15 +628,15 @@ export interface XMLBuilder {
*/
ele(obj: ExpandObject): XMLBuilder

/**
* Creates new element nodes from the given JS object and appends it to the
/**
* Creates new element nodes from the given XML document string appends it to the
* list of child nodes.
*
* @param obj - a JS object representing nodes to insert
* @param content - an XML document string representing nodes to insert
*
* @returns the last top level element node created
*/
ele(obj: ExpandObject, parser: ((obj: ExpandObject) => XMLBuilder)): XMLBuilder
ele(content: string): XMLBuilder

/**
* Removes this node from the XML document.
Expand Down Expand Up @@ -1321,4 +1405,4 @@ type RecursivePartial<T> = {
T[P] extends (infer U)[] ? RecursivePartial<U>[] :
T[P] extends object ? RecursivePartial<T[P]> :
T[P]
}
}
81 changes: 56 additions & 25 deletions src/readers/BaseReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,49 @@ export abstract class BaseReader<U extends string | ExpandObject> {
protected _builderOptions: XMLBuilderOptions

/**
* Initializes a new instance of `BaseWriter`.
* Initializes a new instance of `BaseReader`.
*
* @param builderOptions - XML builder options
*/
constructor(builderOptions: XMLBuilderOptions) {
this._builderOptions = builderOptions
if (builderOptions.parser) {
Object.assign(this, builderOptions.parser)
}
}

abstract _parse(node: XMLBuilder, obj: U): XMLBuilder
abstract _docType(name: string, publicId: string, systemId: string): XMLBuilder | undefined
abstract _comment(parent: XMLBuilder, data: string): XMLBuilder | undefined
abstract _text(parent: XMLBuilder, data: string): XMLBuilder | undefined
abstract _instruction(parent: XMLBuilder, target: string, data: string): XMLBuilder | undefined
abstract _cdata(parent: XMLBuilder, data: string): XMLBuilder | undefined
abstract _element(parent: XMLBuilder, name: string): XMLBuilder | undefined
abstract _attribute(parent: XMLBuilder, name: string, value: string): XMLBuilder | undefined

/**
* Produces an XML serialization of the given node.

_docType(parent: XMLBuilder, name: string, publicId: string, systemId: string): XMLBuilder | undefined {
return parent.dtd({ name: name, pubID: publicId, sysID: systemId })
}

_comment(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.com(data)
}

_text(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.txt(data)
}

_instruction(parent: XMLBuilder, target: string, data: string): XMLBuilder | undefined {
return parent.ins(target, data)
}

_cdata(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.dat(data)
}

_element(parent: XMLBuilder, name: string): XMLBuilder | undefined {
return parent.ele(name)
}

_attribute(parent: XMLBuilder, name: string, value: string): XMLBuilder | undefined {
return parent.att(name, value)
}

/**
* Main parser function which parses the given object and returns an XMLBuilder.
*
* @param node - node to recieve parsed content
* @param obj - object to parse
Expand All @@ -36,18 +60,20 @@ export abstract class BaseReader<U extends string | ExpandObject> {
}

/**
* Used by derived classes to create a DocType node.
* Creates a DocType node.
* The node will be skipped if the function returns `undefined`.
*
* @param name - node name
* @param publicId - public identifier
* @param systemId - system identifier
*/
docType(name: string, publicId: string, systemId: string): XMLBuilder | undefined {
return this._docType(name, publicId, systemId)
docType(parent: XMLBuilder, name: string, publicId: string, systemId: string): XMLBuilder | undefined {
return this._docType(parent, name, publicId, systemId)
}

/**
* Used by derived classes to create a comment node.
* Creates a comment node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
Expand All @@ -57,54 +83,59 @@ export abstract class BaseReader<U extends string | ExpandObject> {
}

/**
* Used by derived classes to create a text node.
* Creates a text node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
*/
text(parent: XMLBuilder, data: string) {
text(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return this._text(parent, data)
}

/**
* Used by derived classes to create a processing instruction node.
* Creates a processing instruction node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param target - instruction target
* @param data - node data
*/
instruction(parent: XMLBuilder, target: string, data: string) {
instruction(parent: XMLBuilder, target: string, data: string): XMLBuilder | undefined {
return this._instruction(parent, target, data)
}

/**
* Used by derived classes to create a CData section node.
* Creates a CData section node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param data - node data
*/
cdata(parent: XMLBuilder, data: string) {
cdata(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return this._cdata(parent, data)
}

/**
* Used by derived classes to create an element node.
* Creates an element node.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param name - node name
*/
element(parent: XMLBuilder, name: string) {
element(parent: XMLBuilder, name: string): XMLBuilder | undefined {
return this._element(parent, name)
}

/**
* Used by derived classes to create an attribute or namespace declaration.
* Creates an attribute or namespace declaration.
* The node will be skipped if the function returns `undefined`.
*
* @param parent - parent node
* @param name - node name
* @param value - node value
*/
attribute(parent: XMLBuilder, name: string, value: string) {
attribute(parent: XMLBuilder, name: string, value: string): XMLBuilder | undefined {
return this._attribute(parent, name, value)
}

Expand Down
9 changes: 5 additions & 4 deletions src/readers/JSONReader.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { XMLBuilder } from "../interfaces"
import { ObjectReader } from "./ObjectReader"
import { BaseReader } from "./BaseReader"

/**
* Parses XML nodes from JSON string.
* Parses XML nodes from a JSON string.
*/
export class JSONReader {
export class JSONReader extends BaseReader<string> {

/**
* Parses the given document representation.
*
* @param node - node receive parsed XML nodes
* @param str - JSON string to parse
*/
parse(node: XMLBuilder, str: string): XMLBuilder {
return new ObjectReader(node.options).parse(node, JSON.parse(str))
_parse(node: XMLBuilder, str: string): XMLBuilder {
return new ObjectReader(this._builderOptions).parse(node, JSON.parse(str))
}
}
32 changes: 4 additions & 28 deletions src/readers/ObjectReader.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,22 @@
import { XMLBuilder, ExpandObject, XMLBuilderOptions } from "../interfaces"
import { XMLBuilder, ExpandObject } from "../interfaces"
import {
isArray, isString, isFunction, forEachArray, isSet, isMap, isObject,
forEachObject, isEmpty
} from "@oozcitak/util"
import { XMLBuilderImpl } from "../builder/XMLBuilderImpl"
import { BaseReader } from "./BaseReader"

/**
* Parses XML nodes from objects and arrays.
* ES6 maps and sets are laso suupoted.
* ES6 maps and sets are also supoorted.
*/
export class ObjectReader extends BaseReader<ExpandObject> {

_docType(name: string, publicId: string, systemId: string): XMLBuilder | undefined {
/** @inheritdoc */
docType(parent: XMLBuilder, name: string, publicId: string, systemId: string): XMLBuilder | undefined {
// document type nodes cannot be represented in a JS object
return undefined
}

_comment(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.com(data)
}

_text(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.txt(data)
}

_instruction(parent: XMLBuilder, target: string, data: string): XMLBuilder | undefined {
return parent.ins(target, data)
}

_cdata(parent: XMLBuilder, data: string): XMLBuilder | undefined {
return parent.dat(data)
}

_element(parent: XMLBuilder, name: string): XMLBuilder | undefined {
return parent.ele(name)
}

_attribute(parent: XMLBuilder, name: string, value: string): XMLBuilder | undefined {
return parent.att(name, value)
}

/**
* Parses the given document representation.
*
Expand Down
Loading

0 comments on commit 0632d4e

Please sign in to comment.