154 lines
5.9 KiB
TypeScript
154 lines
5.9 KiB
TypeScript
const errors = require("./errors.js");
|
|
|
|
import { ParseErrors, ParseResults, Parsers, RemoteSource } from "./freeDatas2HTMLInterfaces";
|
|
|
|
export class ParserForJSON implements Parsers
|
|
{
|
|
private _datasRemoteSource: RemoteSource={ url:"" };
|
|
private _datas2Parse: string="";
|
|
private _parseResults: ParseResults|undefined=undefined;
|
|
|
|
// Revoir tous les protocoles possibles pour une source distante (http(s)://.. , ftp..)
|
|
set datasRemoteSource(source: RemoteSource)
|
|
{
|
|
if(source.url.trim().length === 0)
|
|
throw new Error(errors.parserNeedUrl);
|
|
else
|
|
{
|
|
source.url=source.url.trim();
|
|
this._datasRemoteSource=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 headers=new Headers();
|
|
if(parser._datasRemoteSource.headers !== undefined)
|
|
{
|
|
for(let header of parser._datasRemoteSource.headers)
|
|
headers.append(header.key, header.value);
|
|
}
|
|
const credentials : RequestCredentials|undefined=(parser._datasRemoteSource.withCredentials) ? "include" : "omit";
|
|
const settings={
|
|
method: "GET",
|
|
headers: headers,
|
|
credentials: credentials,
|
|
};
|
|
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);
|
|
}
|
|
}
|
|
} |