regstr
Version:
JSON.stringify objects with RegExp properties and then JSON.parse json string resulted back into original objects. Converts RegExp object to be serializable - into pair of strings (key,value). Could be used for RegExp being bilaterally stringified and ge
605 lines (456 loc) • 22.4 kB
Plain Text
node ./docs/explain_regstr.js
Package <regStr>
(serialization of objects and arrays containing regular expression objects.
JSON.stringify - JSON.parse - compatible)
Converts single RegExp object or some object with RegExp properties
to the form adequate to stringify and back parse them using JSON
by means of JSON.stringify and then JSON.parse back.)
----- Ciphering or conversion ------
The main concept is to cipher RegExp object into 'cipher' pair of strings
or cipher object - single property object, easily stringifiable,
and automatically recognizable after back parsing to get "initial"
object contained RegExp-s.
The easiest way to get acquaintance with module is to look at examples:
Loading handler from working directory ( using variable - h presumes
that module is a handler. Identifier regstr or any yourVar would be OK
as well):
var h=require('./regstr').regStr;
ciphering is provided by method h.streger() , like this:
var v=/asdf/gi;
/asdf/gi
h.streger( v ) or h.streger(/asdf/gi)
gives
{ RE15: 'asdfRE15gi' }
or an object
var o={a:/asdf/gim};
h.streger(o) or h.streger( {a:/asdf/gim} )
gives
{ aRE51: 'asdfRE51gmi' }
or an object inside an array
h.streger( [ {a:/asdf/gi},/^[+]+cc.*$/gim ] );
returns
[ { aRE05: 'asdfRE05gi' }, { '1RE86': '^[+]+cc.*$RE86gmi' } ]
or array with RegExp objects elements
h.streger( [
/asdf/gi,
/^[+]+cc.*$/gim,
/sdf/
]
);
[ { RE21: 'asdfRE21gi' },
{ '1RE19': '^[+]+cc.*$RE19gmi' },
{ '2RE87': 'sdfRE87' } ]
or object with RegExp objects properties
h.streger(
{ a: /asdf/gi,
b: /^[+]+cc.*$/gim,
ab:[{ bb:/sdfg/mg},/sdf/]
}
);
{ ab: [ { bbRE65: 'sdfgRE65gm' }, { '1RE55': 'sdfRE55' } ],
aRE23: 'asdfRE23gi',
bRE96: '^[+]+cc.*$RE96gmi' }
all these results are easily stringified and back parsed by
JSON.stringify and JSON.parse
Applying h.reger() method after parsing will bring RegEpx:
keeping in mind the process flow
ciphering deciphering
RegExp > h.streger > cipherObject > h.reger > RegExp
h.reger( h.streger({ a: /asdf/gi,
b: /^[+]+cc.*$/gim,
ab:[{ bb:/sdfg/mg},/sdf/] } )
);
gives:
{ ab: [ { bb: /sdfg/gm }, /sdf/ ],
a: /asdf/gi,
b: /^[+]+cc.*$/gim }
You already could have observed the letters 'RE' and few (two) digits after
it in strings representing keys and values ciphered. This is a mark
using in ciphering and identification ciphered regExp later. Digits are
random and their number could be changed by property h.nMin. For example
let's set it to 5.
h.nMin=5;
and the lay out of key and value strings will have changed. For ex.:
h.streger( { a: /asdf/gi,
b: /^[+]+cc.*$/gim,
ab:[{ bb:/sdfg/mg},/sdf/],
c:{re:/nnn.+$/gim,ar:[/aaa/,/bbb/g]}
}
);
returns:
{ ab: [ { bbRE06454: 'sdfgRE06454gm' }, { '1RE36099': 'sdfRE36099' } ],
c: { ar: [ [Object], [Object] ], reRE06638: 'nnn.+$RE06638gmi' },
aRE59825: 'asdfRE59825gi',
bRE00611: '^[+]+cc.*$RE00611gmi' }
The explanation regarding REn follows bellow. Here we just mention
that during deciphering different key-value pairs ciphering different
regexp-s could have different number of that random digits after 'RE'
like this:
var b=[ { aRE28: 'asdfRE28gi' }, { '1RE62555': '^[+]+cc.*$RE62555gmi' } ]
h.reger( b); will return
[ { a: /asdf/gi }, /^[+]+cc.*$/gim ]
Handler h (regstr) has h.replacer and h.reviver
methods that could be used to serialize and parse back objects and
arrays with RegExp-s without
direct intermediary of h.streger(...) and h.reger(...).
functions could be used
var replacer = h.replacer.bind(h);
var reviver = h.reviver.bind(h);
and
o -> o1 = JSON.stringify(o,replacer) ->
o = JSON.parse(o1,reviver)
compare o
[ { a: /asdf/gi }, /^[+]+cc.*$/gim ]
with
o=JSON.parse ( JSON.stringify(o,h.replacer.bind(h)), h.reviver.bind(h))
or
o=JSON.parse ( JSON.stringify(o,replacer), reviver)
[ { a: /asdf/gi }, /^[+]+cc.*$/gim ]
and with
h.reger( h.streger([{a:/asdf/gi},/^[+]+cc.*$/gim]) );
[ { a: /asdf/gi }, /^[+]+cc.*$/gim ]
Explanation:
Let's pO is some parent object with a property described by
(key,value) pair, where
value=pO[key]; // and value is regular expression object
(value instanceof RegExp===true)?true:false; // is true and
value=new RegExp(body,mig); // where body and mig are strings
{string}body - is string of regular expression and
{string}mig - RegExp flags - ( multi-line, insensitive, global)
Conversion algorithm features:
1. Additional property key1 is added to object pO -
pO[key1]=value1;
key1=key+RE; // value=new RegExp(body,mig) are converted into string
value1=body+RE+mig; // where
RE='RE'+rndm10;
and {string}rndm10 is random string consisted of 10 random digits
from 0-9 in each place, for ex.
rndm10='0987654321'; //
The number of random digit depends and could be any of your choice
rndm4, ..rndm2 ...
Obviously pO[key1] is serializable by JSON.
2. Or
delete pO[key];
or if you would not have done it yourself the JSON.stringify will do
during serialization
Inclusion of RE string into key1 and value1 is used as a mark
marking the property and permits to identify it during reverse
parse procedure and to transform
value1 string body+RE+mig into new RegExp(body,mig) :
Back Parsing features:
After JSON.parse( regExpJsoned )
1. Selects object string property containing in key and value
marking string RE==='RE'+rndmN ,
where N is number of digital positions in random rndmN N-digital string.
RE populates the ending positions of key( property name string) and
separates body-part from mig-part in property string value===body+RE+mig, so
var vs=pO[oid+RE].split(RE); // (5)
var newRegExpValue= new RegExp( vs[0],vs[1]); // (6)
when you would be needed to get RE you should look for a property
with string value, in (key,value) pair :
and
1. property name and property string value contain the same pattern RE
2. the property name has it located at the end of string
3. the value string consists of three parts: body,RE and mig
splitting the valueString by RE pattern body and mig -parts could be got
and therefor RegExp value be invoked
Package Usage for pO object:
var regstr=require('regstr).regStr; // handler object
variant A.
RegExp property conversions into string:
For each property being RegExp new converted string property
is created. Original ones keep safe.( are not deleted from object
if it's necessary do it yourself or use optional parameter opt_ciphMode
to manage this option)
pO=regstr.streger(pO); // Remark: changes of pO properties are accessible
// for external scope of stregger. Possible call is
// regstr.streger(pO);
variant B.
per single RegExp property transformation
var o;
// preJson step
for(var k in pO){
if( pO[k] instanceof RegExp){
o=regstr.reStr(pO[k],k);
// creates new property with new key and value {string}
pO[o.ky]=o.v;
}
}
// json.stringify
var jsonPo=JSON.stringify(pO)
// parse Back
detailed explanation see in deciphering_explain.txt of deciphering_explain.js-object
in docs directory.
to get it in console type: npm run explain
or inside node by require('./docs.deciphering_explain.js'); command
var oP=JSON.parse(jsonPo);
postParse properties' conversion into RegExp-s if any
variant 1.
All cyphered properties if are available are selected
and are converted to RegExp with new keys (by excluding RE from key):
oP=regstr.reger(oP);
// or Variant 2
// if 'property by property' handling is necessary
var ob;
for( var ip in oP){
if( typeof oP[ip]==='string'){
ob=regstr.regUp(oP[ip]);
if(ob.key!==ip){
// new RegExp property of oP
ob[ob.key]=ob.value;
delete oP[ip];
}
}
}
Remark 1.
RegExp object variable itself could be converted into
object with one property {RE:regString}
var regE=someRegExp; // someRegExp=new RegExp(body,mig);
var oStrReg=regstr.reStr(regE);
// var o=oStrReg.o(); where o={ RE: regString } where
// RE='RE'+rndmN , regString = body+RE+mig,
// at the same time RE=oStrReg.re;
// regString=oStrReg.c;
// body=oStrReg.b;
// mig=oStrReg.mig
//
Remark 2.
uncycle package extension. (miss it now, go further up to Decipher
method concept...)
This RegExp handling algorithm could be included in preStringify methods
of <uncycle> package -targeting to stringify objects with circular references
to provide extending functionality, or when json is used for cloning objects.
to switch it on set
unCycle.uiDirect.regOn=true;
.................................
Following comments are based on notion of uncycle package.
suppose some ith-property with (oid,uid,value) being RegExp
oid='oid'; // property's object id or key
uid='##x#x#...#oid'; // appropriate universal identifier of this property
If pO is this property's parent object,
property's value is RegExp object
pO[oid]=new RegExp(body,mig); // (1) {RegExp}
unCycle.uiDirect[uid]=new RegExp(body,mig); // (2) {RegExp}
to create new property for parent object with string value:
pO[oid+RE]=body+RE+mig; // (3) {string}
where RE='RE'+rndm10
and
rndm10='0987654321'; // -random string of 10 digits from 0-9 in each place
The number of random digit depends and could be any of your choise
rndm4, ..rendm2 ...
2. delete pO[oid]; // if you would no have done it yourself
// JSON.stringify will not do it but only
// would set empty appropriate object
3. appropriately, the uid of this new property will be
uidNewPr=uid+RE; // {string} and uiDirect object will contain property
unCycle.uiDirect[uid+RE]=body+RE+mig; // (4) {string}
Inclusion of RE string into oid and string value of newly created property
simplifies it's identification and following transformation of
body+RE+mig string into new RegExp(body,mig) :
var vs=pO[oid+RE].split(RE); // (5)
var newRegExpValue= new RegExp( vs[0],vs[1]); // (6)
when you would be need to get RE you should look for a property
with string value, i.e. for (key,value) :
(typeof value === 'string') && three remarkable points:
1. For newly created property - property name and
property string ciphered value contain the same pattern RE
2. the property name has it at the end of string
3. the value's body and mig string could be obtained splitting the
valueString by RE pattern
...........................................
---- Decipher method concept and notions : -----
Definitions and notions used below:
parent object pO. Object or array having subobject(s) ciphered
pO={..., o:{},...} or
pO=[..., o, ...]
object - entity containing cipher (cipher pair or cipher object)
pO & o subordination keeps after deciphering
Deciphering converts
cipher pair (keyRe,v1ReV2) or cipher object {keyRe:v1ReV2}
into RegExp objects integrated in appropriate structure
(variable, object(or array), parent object (or array)
cipher separator string RE
RE="RE"+rndmN (1)
rndmN - random part; string consisting only of N random digits (0-9)
keyRe - key-cipher string composed of string key and string RE
keyRe=key+RE (2)
v1ReV2 - RegExp value ciphering string. Three parts string:
v1ReV2=v1+RE+v2, (3)
where RE string is used as separator
to select v1 and v2 strings (body string and modifier strig) determining
regular expression object by following way -
rE= new RegExp(body,modifier) or
rE= new RegExp(v1,v2) (4)
keyRe string and RE separator string are used for ciphering Regular expression
being property of object or element of array object
object property case:
o={key:RegExp} is ciphered as o={keyRe:v1Rev2} (5.1)
or in opposite direction
o={keyRe:v1Rev2} is deciphered in o={key:RegExp} (5)
o={...,keyRe:v1ReV2,..} -> ={...,key:new RegExp(v1,v2)}, ...} (6)
For ciphering naked regular expression as value of variable or array's element
cipher object is used with key="". That is cipher pair is (RE,v1+RE+v2) or
cipher object {RE:v1ReV2}={"'RE'+rndmN":v1+'RE'+rndmN+v2}
RE is used instead of keyRE in key string part.
So for array
a=[...,{RE:v1ReV2},...] is deciphered into a=[...,rE,...], (7)
where rE=new RegExp(v1,v2) (8)
variable
v={RE:v1ReV2} is deciphered into v=rE (9)
subproperty o
pO={...,o:{RE:v1ReV2},...} is converted in pO={...,o:rE,...} (10)
at the same time if key is used
pO={...,o:{keyRe:v1ReV2},...} is converted in pO={...,o:{key:rE},...} (11)
for array element as an object:
pO=[...,{keyRe:v1ReV2},...] -> pO=[...,{key:rE},...] (12)
in the case of arrays key could have digital form key=n - interger, what in this case
would mean index number of deciphered result.
For example if we have cipher {nRE:v1ReV2} as element with index ind of array pO
ind <-index of cipher
pO=[...,...,{nRe:v1ReV2},...] is converted into pO=[...,rE,...,...] (13)
index of rE element -> n
cipher Objects are removed from resulting deciphered objects or arrays.
Remark 1.
nRE form of key-code used for ciphering objects is deciphered in 'n' key letter string
expressing digit(s) for ex. o={...,"23RE123":v1+'RE123'+v2},...} will be deciphered in
o={...,"23":new RegExp(v1,v2),...}
Remark 2.
RE form of keyRe could be used for ciphering of object property only if object has
single property, i.e.
o={"RE12":v1+"RE12"+v2} (14)
is permissible and is converted into o=new RegExp(v1,v2) (14.1)
but
o={a:'a',...,"RE12":v1+"RE12"+v2, ...} is prohibited (15)
and will throw Error!
The table below uses some abbreviations for shortness.
abbreviations:
k - key
R - RE
vRv2 - v1REv2
pO - parent object
rE - new RegExp(v1,v2)
o - object containing cipher -
or cipher pair (key,value) as object property with string value
{...,kR:vRv2,..}
or cipher object with single property {kR:vRv2}
v - variable
a - array
different keys nomination:
kR - keyRE - key ciphered = key+"RE"+rndmN
nR - nRe - the same as kR but key part consists of digits only
R - RE - when key part ="" or undefined
Table. Algorithm explanation. Ciphered entities and their decipherings:
entity | ciphered | deciphering |equation| comments
| and actions
variable:
v={R:vRv2} | v=rE (T1) (rE= new RegExp(v,v2); )
object:
o={kR:vRv2} | o={k:rE}; (T2)
| 1. delete o[kR]
| 2. o[k]=rE before assignment checks if o already has
| property k as instance of RegExp equivalent
| to rE (see equation (3) item 2. ). If this is
| the case nothing new is inserted.
|
o={...,kR:vRv2, ... } | o={..., k:rE, ...}; (T3)
| 1. delete o[kR];
| 2.
| if(!o.hasOwnPrperty(k)||
| o[k] instanceof RegExp!==true
| || o[k]!==rE){ // (*)!! new RegExp(a,b)!== new RegExp(a,b)
| o[k]=rE; // (o[k].source !== rE.source ||
| } // regstr.migV(o[k])!==regstr.migV(rE));
|
o={...,nR:vRv2, ... } | o={...,"n":rE, ...}; (T4) property name consists of digits only
| 1. delete o[nR];
| 2. o["n"]=rE; !! peculiarities similar to (3) 2. !!
|
o={..., R:vRv2, ... } | !! PROHIBITED !! (X) only variable form (1) is possible
| or for object containing ciphered pair
| (Object.keys(o).length===1)===true
or | like in the case when o is
subObject of pO |
| (Object.keys(o).length==1) ===true
pO={...,o:{R:vRv2},...} | pO={ ... ,o:rE, ... } (T5) which is similar to case (T1)
|
Array is pO(parent |
object): |
ind | ind element of pO with index ind
pO=[... ,{kR:vRv2},...] | pO=[...,{k:rE}, ... ] (T6)
| 1. pO[ind]={k:rE};
ind | index == ind
pO=[... ,{nR:vRv2},...] | pO=[...,..,rE,...] (T7) new element of pO with index n
| n index == n
|
| if((pO[n] instanceof RegExp)!==true) // has RegExp element with index n
| ||( value equivalent to rE)!==true){
| 1. pO.splice(n,0,rE);
| }else{
| then nothing is added and
| 2. if(ind<n){
| pO.splice(ind,1); // removes cipher element from array
| }else{
| pO.splice(ind+1,1);
| }
| }
ind | ind
pO=[... ,{R:vRv2},...] | pO=[... ,rE, ... ] (T8)
* ---- Reconversion - deciphering scheme: ---- *
Procedure verifies objects or arrays looking for "ciphered"
properties or elements
The Mark of 'cipher' presence:
- for Object's property:
property name has form keyRE=key+RE,
property value is string of the model: v1ReV2=v1+RE+v2,
where key - is the name of RegExp property ciphered with
value=new RegExp(v1,v2)
"Ciphered" means that it is a "cipher-object" or "cipher"
cipher={keyRE:vReV2} coded RegExp object property
{...,key:new RegExp(v1,v2),...}
or single property object={keyRE:vReV2}
A.Ciphered properties of object;
B.Element(s) of array "ciphered" in the form of single property object
{keyRE:v1ReV2} or {nRe:v1Rev2} (see <Digital key practice> paragraph
regarding nRe form of key;
C.Variable being RegExp ciphered by means of cipher object without key, i.e.
Cipher without key has format {RE:vReV2}, where RE='RE'+rndmN,
rndmN - string of N random digits. Deciphered RegExp will be:
new RegExp(v1,v2)
--- "Digital keys practice": ---
The notion "Digital key" means property name consisting only of digital
characters. For ex.: '1','12', '234' ... something conforms regular
expression /^d+$/.
Let's use short variable form <n> as notation of a Digital key.
Here is an example of digital key in single property object -
{"n":new RegExp(v1,v2)} with value as RegExp object.
Decipher converts cipher object into RegExp.
Deciphering procedure uses single property digital key object as
Intermediary in conversion. Cipher object like {nRe:v1ReV2} transforms
into {n:new RegExp(v1,v2}, is stored in Depo and than converts into
RegExp object. Depo keeps key-value pairs ordered by digital keys values.
to fulfill final conversion.
Decipher having found digital key single property object as an array's
element will transfer it into an element with value new RegExp(v1,v2)
and index n.
i index n index
pO=[...,{nRe:v1Rev1},... ] ==> [ ..., new RegExp,...]
n could not be equal to i, but typically is when deciphering is reverse
procedure of previous ciphering:
[...,rE,...] ->cipher-> [...,{iRe:v1ReV2},..] ->decipher -> [...,rE,..]
Due to some reasons parent array in ciphered form could have few
cipher objects with
- equal digital keys and equal/not equal regExp values
- or has one object with key,value pairs as separate properties
Decipher follows convention:
1.index number got from digital key prevails indices of existed elements,
but, if parent array already has element with RegExp value and index
coinciding that determined by digital key, new element is not added.
2.few cipher objects having equal digital keys combined in common array
who itself becomes an element of parent array with index equal to digital
key value, i.e. [..,{k1:rE1},{k1:rE2,...,{k1:rE3}] converts into ->
[..., [rE1,rE2,rE3],...],where sub-array with rE-s is an element of
parent array and has index k1.
3.When cipher object being element of parent array has few cipher pairs
properties [...{k1:re1,k2:re2,k3:re3},...] it will be converted into
[...,re1,...,re2,re3], where elements re1,re2,re3 have indices k1,k2,k3
appropriately.