Skip to content

Commit

Permalink
Corrected bug in DataTableXmlSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
crisfervil committed Mar 27, 2017
1 parent e92eb32 commit 9f20837
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 4 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added Save to Excel capabilities to the [DataTable](https://dynamicsnode.js.org/DataTable.html) object. See DataTableSerializer object.
- Lots of refactoring to make the code more SOLID
- Added SetBusinessSystemUserRequest message and SecurityHelper class.
- Corrected bug in DataTableXmlSerializer on the serialization of the attribute names.

0.0.18

Expand Down
5 changes: 3 additions & 2 deletions src/DataTableXmlSerializer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IDataTableSerializer } from './IDataTableSerializer'
import { DataTable } from './DataTable';
import { XmlEncode } from './XmlEncode';
import XMLWriter = require('xml-writer');
import et = require('elementtree');

Expand Down Expand Up @@ -33,7 +34,7 @@ export class DataTableXmlSerializer implements IDataTableSerializer {
strValue = this.serializeValue(propValue);
}
if (strValue !== null) {
xw.startElement(propName);
xw.startElement(XmlEncode.encodeName(propName));
if (propType !== null && propType !== undefined) {
xw.writeAttribute("type", propValue.type);
}
Expand Down Expand Up @@ -65,7 +66,7 @@ export class DataTableXmlSerializer implements IDataTableSerializer {
var rowFieldElements = rowElement.getchildren();
for (var j = 0; j < rowFieldElements.length; j++) {
var rowFieldElement = rowFieldElements[j];
var fieldName = rowFieldElement.tag;
var fieldName = XmlEncode.decodeName(rowFieldElement.tag);
var fieldValue = rowFieldElement.text;
var fieldType = rowFieldElement.attrib["type"];
var parsedValue = this.parseXmlValue(fieldValue);
Expand Down
84 changes: 84 additions & 0 deletions src/XmlEncode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
export class XmlEncode{

// https://www.w3.org/TR/REC-xml/#sec-common-syn
// http://www.java2s.com/Code/Java/XML/EncoderandDecoderforXMLelementandattributenames.htm
// http://www.fileformat.info/info/charset/UTF-16/list.htm

static XML_NAME_NOT_ALLOWED_CHARACTERS = [' ','<','>','"','\'','&',';'];
static XML_BEGINNING_NAME_NOT_ALLOWED_CHARACTERS = ['0','1','2','3','4','5','6','7','8','9'];

static encodeName(value:string):string{

var encodedValues: string[] = [];
var encodedName: string = null;

if (value !== null && value.length > 0) {

// first character
if (XmlEncode.XML_BEGINNING_NAME_NOT_ALLOWED_CHARACTERS.indexOf(value[0]) != -1 ||
XmlEncode.XML_NAME_NOT_ALLOWED_CHARACTERS.indexOf(value[0]) != -1) {
encodedValues.push(`_x00${value.charCodeAt(0).toString(16)}_`);
}
else {
encodedValues.push(value[0]);
}

for (var i = 1; i < value.length; i++) {
var char = value[i];

if (XmlEncode.XML_NAME_NOT_ALLOWED_CHARACTERS.indexOf(value[i]) != -1) {
encodedValues.push(`_x00${value.charCodeAt(i).toString(16)}_`);
}
else {
encodedValues.push(value[i]);
}
}
}

if (encodedValues.length > 0) {
encodedName = encodedValues.join('');
}

return encodedName;

}

static decodeName(encodedName:string){

var decodedNameParts:string[]=[];
var decodedName:string=null;

if (encodedName !== null && encodedName.length > 0) {

for (var i = 0; i < encodedName.length; i++) {
var char = encodedName[i];
var encodedChar=false;

if (char === '_') {
if (encodedName.length - i > 6 && encodedName[i + 1] === 'x' && encodedName[i + 6] === '_') {
var charCodeStr=encodedName.substr(i+2,4);
var charCode = parseInt(charCodeStr,16);
if (!isNaN(charCode) && isFinite(charCode)) {
encodedChar=true;
var decodedChar = String.fromCharCode(charCode);
decodedNameParts.push(decodedChar);
i+=6;
}
}
}

if(!encodedChar){
// just a regular char not encoded
decodedNameParts.push(char);
}
}
}

if(decodedNameParts.length>0){
decodedName = decodedNameParts.join('');
}

return decodedName;

}
}
4 changes: 2 additions & 2 deletions test/DataTableSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ describe("DataTableSerializer", function() {

// TODO: add different data types
var d1 = new DataTable("myTable");
d1.rows.push({ prop1: Guid.create().toString(), prop2: "value2&" }); // use xml not permitted values
d1.rows.push({ prop1: true, prop2: "val\tue2\n", prop3: "0001" });
d1.rows.push({ prop1: Guid.create().toString(), prop2: "value2&<>'\"" }); // use xml not permitted values
d1.rows.push({ prop1: true, prop2: "val\tue2\n", prop3: "0001", "1 <&\'>\" ":"test" }); // use xml not permitted values as property names
d1.rows.push({ prop1: false, prop2: new Date(), prop3: 12, prop4: 12.5, prop5: "12345" });
d1.rows.push({ prop1: "whatever", prop2: { type: "myType", value: "my value" } });
DataTableSerializer.save(d1,fileName);
Expand Down
43 changes: 43 additions & 0 deletions test/XmlEncode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import assert = require("assert");
import { Guid } from "../src/Guid";
import {XmlEncode} from '../src/XmlEncode';

describe('XmlEncode', function () {
it('Encodes and decodes a Name that begins with a number', function () {
var name = '1 abcdef abcdef';
var encodedName = XmlEncode.encodeName(name);
assert.equal(encodedName,'_x0031__x0020_abcdef_x0020_abcdef');
var decodedName = XmlEncode.decodeName(encodedName);
assert.equal(decodedName,name);
});
it('Encodes and decodes a Name that begins with a space', function () {
var name = ' abcdef abcdef';
var encodedName = XmlEncode.encodeName(name);
assert.equal(encodedName,'_x0020_abcdef_x0020_abcdef');
var decodedName = XmlEncode.decodeName(encodedName);
assert.equal(decodedName,name);
});
it('Encodes and decodes a very short Name', function () {
var name = 'a';
var encodedName = XmlEncode.encodeName(name);
assert.equal(encodedName,'a');
var decodedName = XmlEncode.decodeName(encodedName);
assert.equal(decodedName,name);
});
it('Encodes and decodes a very short encoded Name', function () {
var name = '1';
var encodedName = XmlEncode.encodeName(name);
assert.equal(encodedName,'_x0031_');
var decodedName = XmlEncode.decodeName(encodedName);
assert.equal(decodedName,name);
});
it('Returns null for a null or empty input', function () {
var name = null;
var encodedName = XmlEncode.encodeName(name);
assert.ok(encodedName===null);

var decodedName = XmlEncode.decodeName(encodedName);
assert.ok(decodedName === null);
});

});

0 comments on commit 9f20837

Please sign in to comment.