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 { 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); } } }