FreeDatas2HTML/src/freeDatas2HTMLParserForJSON.ts

146 lines
5.6 KiB
TypeScript

const errors = require("./errors.js");
import { RemoteSources } from "./freeDatas2HTMLRemoteSources";
import { ParseErrors, ParseResults, Parsers, RemoteSource, RemoteSourceSettings } from "./freeDatas2HTMLInterfaces";
export class ParserForJSON implements Parsers
{
private _datasRemoteSource: RemoteSource;
private _datas2Parse: string="";
private _parseResults: ParseResults|undefined=undefined;
// L'instance d'une autre classe que RemoteSource peut être passée au constructeur
constructor(datasRemoteSource?: RemoteSource)
{
if(datasRemoteSource !== undefined)
this._datasRemoteSource=datasRemoteSource;
else
this._datasRemoteSource=new RemoteSources({ url:"" });
}
public setRemoteSource(source: RemoteSourceSettings)
{
this._datasRemoteSource=new RemoteSources(source);
}
get datasRemoteSource() : RemoteSource
{
return this._datasRemoteSource;
}
set datas2Parse(datas: string)
{
if(datas.trim().length === 0)
throw new Error(errors.parserNeedDatas);
else
this._datas2Parse=datas.trim();
}
get datas2Parse() : string
{
return this._datas2Parse;
}
get parseResults() : ParseResults|undefined
{
return this._parseResults;
}
// Refuse les champs qui ne sont pas des chaînes de caractères
// trim() les autres
public static trimAllFields(fields: any[]) : string[]
{
const nb=fields.length, goodFields: string[]=[];
for(let i=0; i < nb; i++)
{
if(typeof fields[i] === "string")
goodFields.push(fields[i].trim());
}
return goodFields;
}
// async dans le cas d'une source distante
public async parse(): Promise<any>
{
const parser=this;
let parseContent="";
if(parser._datasRemoteSource.url !== "")
{
const settings: {}=parser._datasRemoteSource.getFetchSettings();
const response=await fetch(parser._datasRemoteSource.url, settings);
if (! response.ok)
throw new Error(errors.parserRemoteFail);
parseContent=await response.text(); // doit en fait retourner du JSON, mais il est parsé plus loin.
}
else if(parser._datas2Parse !== "")
parseContent=parser._datas2Parse;
else
throw new Error(errors.parserNeedSource);
try
{
const datasParsed=JSON.parse(parseContent);
const typesOkForValue=["boolean","number","string"];
let fields: string[]=[], datas: {[index: string]:string}[]=[], parseErrors: ParseErrors[]=[];
// Je peux recevoir 2 tableaux contenant respectivement la liste de champs : string[] + celle des données : any[][]
if(datasParsed.fields !== undefined && Array.isArray(datasParsed.fields) && datasParsed.datas !== undefined && Array.isArray(datasParsed.datas))
{
fields=ParserForJSON.trimAllFields(datasParsed.fields);
const nbFields=fields.length, nbDatas=datasParsed.datas.length;
for(let i=0; i < nbDatas; i++)
{
const dataObject: {[index: string]: string} = {}, nbObjFields=datasParsed.datas[i].length;
for(let j=0; j < nbObjFields && j < nbFields; j++)
{
if(typesOkForValue.indexOf(typeof datasParsed.datas[i][j]) === -1)
parseErrors.push({ row:i, message:errors.parserTypeError+typeof datasParsed.datas[i][j]});
else
dataObject[fields[j]]=datasParsed.datas[i][j]+""; // force le type String
}
if(Object.keys(dataObject).length !== 0)
datas.push(dataObject);
}
}
else // Ou un tableau d'objets {}[], dont les attributs sont les noms des champs
{
let i=0;
for(let data of datasParsed)
{
// Ici les champs sont découverts au fur et à mesure,
// leur ordre peut être différent d'une ligne à l'autre
// et tous les champs ne sont pas systématiquement présents
let dataObject: {[index: string]: string} = {}
for(let field in data)
{
if(typesOkForValue.indexOf(typeof data[field]) !== -1)
{
field=field.trim();
if(field !== "" && fields.indexOf(field) === -1)
fields.push(field);
dataObject[field]=data[field]+""; // force le type String
}
else
parseErrors.push({ row:i, message:errors.parserTypeError+typeof data[field]});
}
if(Object.keys(dataObject).length !== 0)
datas.push(dataObject);
i++;
}
}
if(fields.length === 0) // possible si données fournies non correctement formées.
throw new Error(errors.parserFail);
// datas et errors peuvent par contre rester vides.
parser._parseResults =
{
datas: datas,
errors: parseErrors,
fields: fields,
};
}
catch(e)
{
console.error(e);
throw new Error(errors.parserFail);
}
}
}