Это моя лучшая попытка, основанная на ответе на аналогичный вопрос .Он использует вашу логику преобразования в число и обратно в строку и проверки, соответствует ли она исходной строке, и применяет ее ко всем числам в JSON.
К сожалению, эта логика сама по себе немного ошибочна, так как естьнесколько способов представить одно и то же число в JSON.Он выдаст ошибку для чисел, которых нет в представлении, заданном Number#toString()
, IE 1e1
является одним из способов представления 10
, но он выдаст RangeError ниже.Если вы можете гарантировать, что ваши номера будут представлены в том же формате, что и Number # toString () , то это должно работать для вас:
const tests = [
// Success cases
`{"foo":[10,25]}`,
`{"\\\\\\\"":[10,25]}`,
`{"99999999999999999999999999":"99999999999999999999999999"}`,
`{"foo":[10,25],"bar":{"baz":{"bark":[1,2,3]}}}`,
// RangeError cases
`{"foo":99999999999999999999999999}`,
`{"99999999999999999999999999":99999999999999999999999999}`,
`{"foo":[10,25],"bar":{"baz":{"bark":[1,2,3,1e1]}}}`,
];
tests.forEach( test => {
try {
console.log( 'Success', JSON.stringify( safeDecodeJson( test ) ) );
} catch ( e ) {
console.log( 'Error', e.message );
}
} );
function safeDecodeJson( str ) {
const prefix = '^({{SafeJsonDecode}})^:';
const pre_decode = str.replace( /((?:[^"]*?(?:"(?:[^"\\]|\\.)*?")?)*?)((?:[:,\[]|^)[\s\n]*)(-?(0|([1-9]\d*))(\.\d+)?([eE][-+]?\d*)?)/gsy, `$1$2"${prefix}$3"` );
return JSON.parse( pre_decode, ( key, value ) => {
if ( typeof value !== 'string' || ! value.startsWith( prefix ) )
return value;
const numeric_string = value.substr( prefix.length );
if ( '' + +numeric_string !== numeric_string )
throw new RangeError( `\`${numeric_string}\` out of range or not in canonical form` );
return +numeric_string;
} );
}
Если вы хотите, вы можете использовать библиотеку чисел произвольной точности, такую как bignumber.js , для анализа чисел вместо выдачи RangeError.При этом вы также можете обнаружить, что 1e1
совпадает с 10
:
const result1 = safeDecodeJson( '9999999999999999999999999' );
// If out of range the result will be a BigNumber
console.log( BigNumber.isBigNumber( result1 ) );
console.log( result1 );
const result2 = safeDecodeJson( '1e1' );
// If exactly representable by a JavaScript number, the result will be a number, not a BigNumber
console.log( BigNumber.isBigNumber( result2 ) );
console.log( result2 );
function safeDecodeJson( str ) {
const prefix = '^({{SafeJsonDecode}})^:';
const pre_decode = str.replace( /((?:[^"]*?(?:"(?:[^"\\]|\\.)*?")?)*?)((?:[:,\[]|^)[\s\n]*)(-?(0|([1-9]\d*))(\.\d+)?([eE][-+]?\d*)?)/gsy, `$1$2"${prefix}$3"` );
return JSON.parse( pre_decode, ( key, value ) => {
if ( typeof value !== 'string' || ! value.startsWith( prefix ) )
return value;
const numeric_string = value.substr( prefix.length );
if ( '' + +numeric_string !== numeric_string ) {
const big = new BigNumber( numeric_string );
if ( ! new BigNumber( +numeric_string ).isEqualTo( big ) )
return big;
}
return +numeric_string;
} );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/8.1.1/bignumber.min.js"></script>
Вы также можете просто возвращать числа как BigNumber (просто всегда возвращайте new BigNumber( numeric_string )
вместо условного возврата +numeric_string
.