簡體   English   中英

如何解析JSON並在保真度下降時拋出?

[英]How to parse JSON and throw if loss of fidelity?

JSON中的某些值無法完全用JavaScript表示。 例如:

9999999999999999999999999

我正在研究需要互操作性的協議/應用程序,我們使用JSON作為數據交換格式。 在我的JavaScript實現中,我希望JSON解析器可以拋出這些輸入。

我創建了一個簡單的(錯誤的)函數來執行此操作。

function safeDecodeJson(str) {
  decoded = JSON.parse(str);
  reencoded = JSON.stringify(decoded);
  if (str != reencoded) {
    throw new RangeError();
  }
  return decoded;
}

這是一個測試案例:

jsonString = "9999999999999999999999999";
safeDecodeJson(jsonString);

它確實引發了RangError。

我的問題是,只有輸入最少時,此safeDecodeJson函數才能工作。 有沒有更強大的方法來實現此功能?


具體來說,我擔心對輸入JSON文件的“非注入式攻擊”。 我的系統要求邏輯上不同的JSON輸入(例如9999999999999999999999999和9999999999999999999999998)在JavaScript中具有不同的表示形式。 否則函數必須拋出。

這並不奇怪。 Javascript內部使用浮點。 是最高值的說明。

換句話說,您不能使用超過53位。 在某些實現中,您可能會限制為31。嘗試使用bignum庫,或者,如果只需要處理整數,則嘗試使用biginteger庫。

已經給出的答案是沒有答案的問題。 閱讀問題。 有沒有更強大的方法來實現此功能? 我想問一下,當序列化為JSON時,是否能夠以字符串形式呈現數字,以便JavaScript JSON解析器不會嘗試解析它。 然后,您可以在JSON.parse手動處理奇數情況之后-您可以手動解析單個字符串 'd,然后進行相反的操作以查看它們是否相同。 使用BigInt或其他任何方法都可以,但這不是您要的。

這是我的最佳嘗試,基於對類似問題的回答 它使用轉換為數字然后返回字符串的邏輯,並檢查它是否與原始字符串匹配並將其應用於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。 這樣,您還可以檢測到1e110相同:

 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM