JSON bỏ qua Infinity và NaN; Trạng thái JSON trong ECMAScript?


180

Có ai biết tại sao JSON lại bỏ NaN và +/- Infinity không? Nó đặt Javascript vào tình huống kỳ lạ nơi các đối tượng có thể tuần tự hóa, nếu không, chúng chứa các giá trị vô cực NaN hoặc +/-.

Có vẻ như điều này đã được đúc bằng đá: xem RFC4627ECMA-262 (phần 24.5.2, JSON.opesify, NOTE 4, trang 683 của ECMA-262 pdf ở lần chỉnh sửa cuối cùng):

Số hữu hạn được xâu chuỗi như thể bằng cách gọi ToString(number). NaN và Infinity bất kể dấu hiệu nào được biểu diễn dưới dạng Chuỗi null.


Tôi không thể tìm thấy trích dẫn đó trong một trong hai tài liệu.
wingbedubmariner 24/08/2015

1
Đã sửa nó, có vẻ như đã có một tham chiếu cũ / chỉnh sửa cũ.
Jason S

Câu trả lời:


90

InfinityNaNkhông phải là từ khóa hay bất cứ điều gì đặc biệt, chúng chỉ là các thuộc tính trên đối tượng toàn cầu (như vậy undefined) và như vậy có thể được thay đổi. Vì lý do đó, JSON không bao gồm chúng trong thông số kỹ thuật - về bản chất, bất kỳ chuỗi JSON thực sự nào cũng sẽ có kết quả tương tự trong EcmaScript nếu bạn thực hiện eval(jsonString)hoặc JSON.parse(jsonString).

Nếu nó được cho phép thì ai đó có thể tiêm mã giống như

NaN={valueOf:function(){ do evil }};
Infinity={valueOf:function(){ do evil }};

vào một diễn đàn (hoặc bất cứ điều gì) và sau đó bất kỳ việc sử dụng json trên trang web đó có thể bị xâm phạm.


29
Nếu bạn đánh giá 1/0 bạn nhận được Infinity, nếu bạn đánh giá -1/0 bạn nhận được -Infality, nếu bạn đánh giá 0/0 bạn nhận được NaN.
Jason S

9
Nhưng các thuật ngữ NaNInfinitylà tên thuộc tính, vì vậy, trong khi Chuỗi (1/0) tạo ra một chuỗi "Infinity"chỉ là biểu diễn chuỗi của giá trị vô hạn. Không thể biểu thị một trong hai NaNhoặc Infinitycác giá trị bằng chữ là ES - bạn phải sử dụng một biểu thức (ví dụ: 1/0, 0/0, v.v.) hoặc tra cứu thuộc tính (tham chiếu đến Infinityhoặc NaN). Vì những yêu cầu thực thi mã, chúng không thể được bao gồm trong JSON.
olliej

16
Theo quan điểm của bạn về an toàn / bảo mật, tất cả một trình phân tích cú pháp JSON tốt sẽ phải làm khi chuyển đổi NaN là mang lại giá trị 0/0 (thay vì đánh giá ký hiệu NaN) sẽ trả về NaN "thực" bất kể là gì ký hiệu NaN được định nghĩa lại là.
Jason S

33
@olliej: bạn cho rằng NaN không phải là một nghĩa đen, tôi không biết Javascript đủ để đánh giá ngữ nghĩa javascript. Nhưng đối với định dạng tệp lưu trữ số dấu phẩy động chính xác gấp đôi, cần có một cách để xác định số float, tức là bằng một chữ NaN / Infinity / NegInfinity. Đây là các trạng thái của nhân đôi 64 bit và do đó nên được biểu diễn. Có những người phụ thuộc vào họ (vì lý do). Có lẽ họ đã bị lãng quên vì JSON / Javascript bắt nguồn từ phát triển web thay vì tính toán khoa học.
wirrbel

35
Đó là 100%, hoàn toàn SAI cho JSON khi tự ý bỏ qua các trạng thái số dấu phẩy động chuẩn và hợp lệ hoàn toàn của NaN, Infinity và -Infinity. Về cơ bản, JSON đã quyết định hỗ trợ một tập hợp con tùy ý của các giá trị float của IEEE, bỏ qua ba giá trị cụ thể vì chúng cứng hoặc một cái gì đó. Không. Khả năng Eval thậm chí không phải là một cái cớ, bởi vì những con số như vậy có thể đã được mã hóa thành các chữ 1/0, -1/0 và 0/0. Chúng sẽ là các số hợp lệ được gắn vào "/ 0", không chỉ đơn giản để phát hiện, mà còn thực sự được đánh giá là ES cùng một lúc. Không có lý do.
Triynko

56

Về câu hỏi ban đầu: Tôi đồng ý với người dùng "cbare" ở chỗ đây là một thiếu sót đáng tiếc trong JSON. IEEE754 định nghĩa đây là ba giá trị đặc biệt của số dấu phẩy động. Vì vậy, JSON không thể biểu diễn đầy đủ các số dấu phẩy động của IEEE754. Thực tế còn tệ hơn nữa, vì JSON như được định nghĩa trong ECMA262 5.1 thậm chí không xác định liệu các số của nó có dựa trên IEEE754 hay không. Do luồng thiết kế được mô tả cho hàm Stringify () trong ECMA262 có đề cập đến ba giá trị IEEE đặc biệt, nên người ta có thể nghi ngờ rằng ý định trên thực tế là hỗ trợ các số dấu phẩy động của IEEE754.

Là một điểm dữ liệu khác, không liên quan đến câu hỏi: Kiểu dữ liệu XML xs: float và xs: double do trạng thái dựa trên số dấu phẩy động của IEEE754 và chúng hỗ trợ biểu diễn ba giá trị đặc biệt này (Xem W3C XSD 1.0 Phần 2 , Loại dữ liệu).


5
Tôi đồng ý đây là tất cả không may. Nhưng có lẽ một điều tốt là các số JSON không chỉ định định dạng dấu phẩy động chính xác. Ngay cả IEEE754 chỉ định nhiều định dạng - kích thước khác nhau và phân biệt giữa số mũ thập phân và số nhị phân. JSON đặc biệt phù hợp với số thập phân, vì vậy sẽ thật đáng tiếc nếu một số tiêu chuẩn được ghim nó thành nhị phân.
Adrian Ratnapala

5
@AdrianRatnapala +1 Thật vậy: Các số JSON có khả năng chính xác vô hạn, vì vậy tốt hơn nhiều so với thông số kỹ thuật của IEEE, vì chúng không có giới hạn kích thước, không có giới hạn chính xác và không có hiệu ứng làm tròn (nếu trình tuần tự hóa có thể xử lý nó).
Arnaud Bouchez

2
@ArnaudBouchez. Điều đó nói rằng, JSON vẫn nên hỗ trợ các chuỗi đại diện cho NaN và + -Infinity. Ngay cả khi JSON không nên được ghim vào bất kỳ định dạng IEEE nào, mọi người xác định định dạng số ít nhất nên nhìn vào trang wikipedia IEEE754 và dừng lại một chút để suy nghĩ.
Adrian Ratnapala


Điều này không đáng tiếc. Xem câu trả lời của @CervEd. Nó không bị ràng buộc với IEE754, đây là một điều tốt (ngay cả khi hầu hết các ngôn ngữ lập trình sử dụng IEEE754 và do đó yêu cầu xử lý bổ sung trong trường hợp NaN, v.v.).
Ludovic Kuty

16

Bạn có thể điều chỉnh mẫu đối tượng null không và trong JSON của bạn biểu thị các giá trị như

"myNum" : {
   "isNaN" :false,
   "isInfinity" :true
}

Sau đó, khi kiểm tra, bạn có thể kiểm tra loại

if (typeof(myObj.myNum) == 'number') {/* do this */}
else if (myObj.myNum.isNaN) {/* do that*/}
else if (myObj.myNum.isInfinity) {/* Do another thing */}

Tôi biết trong Java bạn có thể ghi đè các phương thức tuần tự hóa để thực hiện một điều như vậy. Không chắc chắn việc xê-ri hóa của bạn đến từ đâu, vì vậy tôi không thể cung cấp chi tiết về cách triển khai nó trong các phương thức tuần tự hóa.


1
hmmm ... đó là một câu trả lời cho một cách giải quyết; Tôi đã không thực sự yêu cầu một cách giải quyết mà là tại sao những giá trị này bị loại trừ. Nhưng dù sao +1.
Jason S

2
@Zoidberg: undefinedkhông phải là một từ khóa, nó là một tài sản trên đối tượng toàn cầu
olliej

2
@Zoidberg: không xác định là một thuộc tính trên đối tượng toàn cầu - nó không phải là một từ khóa, vì vậy "undefined" in thistrả về đúng trong phạm vi toàn cầu. Nó cũng có nghĩa là bạn có thể làm undefined = 42if (myVar == undefined)trở thành (về cơ bản) myVar == 42. Điều này quay trở lại những ngày đầu của javascript nee javascript nơi undefinedkhông tồn tại theo mặc định, vì vậy mọi người chỉ làm var undefinedtrong phạm vi toàn cầu. Do đó, undefinedkhông thể tạo một từ khóa mà không phá vỡ các trang web hiện có, và vì vậy chúng tôi đã cam chịu tất cả thời gian để không xác định là một tài sản bình thường.
olliej

2
@olliej: Tôi không biết tại sao bạn nghĩ không xác định là một tài sản trên đối tượng toàn cầu. Theo mặc định, tra cứu của không xác định là giá trị tích hợp của không xác định. Nếu bạn ghi đè lên nó bằng "undinite = 42" thì khi bạn truy cập không xác định dưới dạng tra cứu biến, bạn sẽ nhận được giá trị ghi đè. Nhưng hãy thử thực hiện "zz = không xác định; không xác định = 42; x = {}; 'không xác định cũ =' + (xa === zz) + ', không xác định new =' + (xa === không xác định)". Bạn không bao giờ có thể xác định lại các giá trị bên trong của null, không xác định, NaN hoặc Infinity, ngay cả khi bạn có thể ghi đè lên tra cứu biểu tượng của chúng.
Jason S

2
@Jason undefinedlà một tài sản toàn cầu vì nó được chỉ định như vậy. Tham khảo 15.1.1.3 của ECMAScript-262 lần 3.
kangax

11

Các chuỗi "Infinity", "-Infinity" và "NaN" đều ép buộc các giá trị mong đợi trong JS. Vì vậy, tôi cho rằng cách đúng để biểu diễn các giá trị này trong JSON là dưới dạng chuỗi.

> +"Infinity"
Infinity

> +"-Infinity"
-Infinity

> +"NaN"
NaN

Mặc định, đó chỉ là một sự xấu hổ JSON.opesify không làm điều này theo mặc định. Nhưng, có một cách:

> JSON.stringify({ x: Infinity }, function (k,v) { return v === Infinity ? "Infinity" : v; })
"{"x":"Infinity"}"

1
0/0, v.v., không hợp lệ JSON. Bạn phải làm việc trong giới hạn của tiêu chuẩn và các chuỗi thực hiện công việc độc đáo.
teh_senaus

Ngược lại, tôi nghĩ đây là giải pháp thực tế duy nhất, nhưng tôi sẽ thực hiện chức năng trả về NaN nếu giá trị đầu vào là "NaN", v.v ... Cách bạn thực hiện chuyển đổi có xu hướng tiêm mã.
Marco Sulla

3
Các giá trị JSON không thể là biểu thức số học ... mục tiêu làm cho tiêu chuẩn tách biệt với cú pháp nghĩa đen của ngôn ngữ là làm cho JSON có thể khử được mà không cần thực hiện bất kỳ mã nào dưới dạng mã. Không chắc chắn tại sao chúng ta không thể có NaNInfinitythêm vào như các giá trị từ khóa như truefalse, mặc dù.
Mark Reed

Để làm cho nó rõ ràng hơn, chúng ta có thể sử dụng Number("Infinity"), Number("-Infinity")Number("NaN")
HKTonyLee

Đây là công việc như ma thuật. JSON.parse("{ \"value\" : -1e99999 }")dễ dàng trở lại { value:-Infinity }trong javascript. Chỉ có điều nó không tương thích với loại số tùy chỉnh có thể lớn hơn thế
Thaina

7

Nếu bạn có quyền truy cập vào mã tuần tự hóa, bạn có thể biểu thị Infinity là 1.0e + 1024. Số mũ quá lớn để thể hiện gấp đôi và khi được khử lưu lượng, nó được biểu diễn dưới dạng Vô cực. Hoạt động trên webkit, không chắc chắn về các trình phân tích cú pháp json khác!


4
IEEE754 hỗ trợ số dấu phẩy động 128 bit nên 1.0e5000 tốt hơn
Ton Plomp

2
Tấn: 128 bit đã được thêm vào sau. Nếu họ quyết định thêm 256 bit thì sao? Sau đó, bạn sẽ phải thêm nhiều số không và mã hiện tại sẽ hoạt động khác đi. Infinitysẽ luôn luôn Infinitynhư vậy, tại sao không hỗ trợ điều đó?
cừu bay

1
Ý tưởng thông minh! Tôi vừa mới chuyển sang một định dạng khác hoặc thêm mã giải pháp rườm rà vào trình phân tích cú pháp của tôi. Không lý tưởng cho mọi trường hợp, nhưng trong trường hợp của tôi, trong đó vô cực đóng vai trò là trường hợp cạnh được tối ưu hóa cho chuỗi hội tụ, nó hoàn hảo và ngay cả khi độ chính xác lớn hơn sẽ được giới thiệu thì nó vẫn hoàn toàn chính xác. Cảm ơn!
Hoặc Sharir

3
1, -1 và 0 ..... các số hoàn toàn hợp lệ / có thể phân tích cú pháp, trở thành ba giá trị đặc biệt đó khi bạn chỉ cần thêm /0vào cuối của chúng. Nó dễ dàng phân tích cú pháp, có thể nhìn thấy ngay lập tức và thậm chí có thể đánh giá được. Không thể giải thích được rằng họ chưa thêm nó vào tiêu chuẩn: {"Not A Number":0/0,"Infinity":1/0,"Negative Infinity":-1/0} << Tại sao không? alert(eval("\"Not A Number\"") //works alert(eval("1/0")) //also works, prints 'Infinity'. Không có lý do.
Triynko


1

IEEE Std 754-2008 hiện tại bao gồm các định nghĩa cho hai cách biểu diễn dấu phẩy động 64 bit khác nhau: loại dấu phẩy động 64 bit thập phân và loại dấu phẩy động 64 bit nhị phân.

Sau khi làm tròn chuỗi .99999990000000006, giống như .9999999trong biểu diễn 64 bit nhị phân của IEEE nhưng nó KHÔNG giống như .9999999trong biểu diễn 64 bit thập phân của IEEE. Trong .99999990000000006vòng dấu phẩy động thập phân 64 bit của IEEE đến giá trị .9999999000000001không giống với .9999999giá trị thập phân .

Do JSON chỉ coi các giá trị số là các chuỗi số có các chữ số thập phân, nên không có cách nào để một hệ thống hỗ trợ cả biểu diễn dấu phẩy động nhị phân và dấu thập phân của IEEE (như Sức mạnh của IBM) để xác định giá trị nào trong hai giá trị dấu phẩy động số có thể của IEEE là dự định.


Điều này có liên quan gì đến câu hỏi? (đó là về Infinity và NaN)
Bryan

1

Cách giải quyết tiềm năng cho các trường hợp như {"key": Infinity}:

JSON.parse(theString.replace(/":(Infinity|-IsNaN)/g, '":"{{$1}}"'), function(k, v) {
   if (v === '{{Infinity}}') return Infinity;
   else if (v === '{{-Infinity}}') return -Infinity;
   else if (v === '{{NaN}}') return NaN;
   return v;
   });

Ý tưởng chung là thay thế các giá trị không hợp lệ bằng một chuỗi chúng ta sẽ nhận ra khi phân tích cú pháp và thay thế nó bằng biểu diễn JavaScript thích hợp.


Tôi không biết tại sao giải pháp này lại bị downvote bởi vì thật lòng mà nói, nếu bạn gặp phải tình huống chuỗi JSON của bạn chứa các giá trị Infinity hoặc IsNaN thì nó sẽ thất bại khi bạn cố phân tích nó. Sử dụng kỹ thuật này, trước tiên, bạn thay thế các lần xuất hiện của IsNaN hoặc Infinity bằng một thứ khác (để tách chúng khỏi bất kỳ chuỗi hợp lệ nào có thể chứa các thuật ngữ đó) và sử dụng JSON.parse (chuỗi, gọi lại) để trả về các giá trị JavaScript hợp lệ, hợp lệ. Tôi đang sử dụng điều này trong mã sản xuất và không bao giờ có bất kỳ vấn đề.
SHamel

Điều này có làm rối tung Infinity bên trong chuỗi không? Đối với nhiều người sử dụng, có thể an toàn khi cho rằng đó không phải là vấn đề, nhưng giải pháp không hoàn toàn mạnh mẽ.
olejorgenb

1

Lý do được nêu ở trang ii trong Tiêu chuẩn ECMA-404 Cú pháp trao đổi dữ liệu JSON, Ấn bản đầu tiên

JSON là bất khả tri về các con số. Trong bất kỳ ngôn ngữ lập trình nào, có thể có nhiều loại số lượng năng lực và bổ sung khác nhau, cố định hoặc trôi nổi, nhị phân hoặc thập phân. Điều đó có thể làm cho việc trao đổi giữa các ngôn ngữ lập trình khác nhau trở nên khó khăn. Thay vào đó, JSON chỉ cung cấp biểu diễn các số mà con người sử dụng: một chuỗi các chữ số. Tất cả các ngôn ngữ lập trình đều biết cách hiểu ý nghĩa của các chuỗi số ngay cả khi chúng không đồng ý với các biểu diễn bên trong. Điều đó là đủ để cho phép trao đổi.

Lý do là không, như nhiều người đã tuyên bố, do các đại diện của NaNInfinitytập lệnh ECMA. Đơn giản là một nguyên tắc thiết kế cốt lõi của JSON.

Bởi vì nó rất đơn giản, nên người ta không hy vọng rằng ngữ pháp JSON sẽ thay đổi. Điều này mang lại cho JSON, như một ký hiệu nền tảng, sự ổn định to lớn


-3

Nếu giống như tôi, bạn không có quyền kiểm soát mã tuần tự hóa, bạn có thể xử lý các giá trị NaN bằng cách thay thế chúng bằng null hoặc bất kỳ giá trị nào khác như một chút hack như sau:

$.get("file.json", theCallback)
.fail(function(data) {
  theCallback(JSON.parse(data.responseText.replace(/NaN/g,'null'))); 
} );

Về bản chất, .fail sẽ được gọi khi trình phân tích cú pháp json ban đầu phát hiện mã thông báo không hợp lệ. Sau đó, một chuỗi thay thế được sử dụng để thay thế các mã thông báo không hợp lệ. Trong trường hợp của tôi, đó là một ngoại lệ cho trình tuần tự trả về các giá trị NaN vì vậy phương pháp này là phương pháp tốt nhất. Nếu kết quả thường chứa mã thông báo không hợp lệ, tốt hơn hết bạn không nên sử dụng $ .get mà thay vào đó để truy xuất thủ công kết quả JSON và luôn chạy thay thế chuỗi.


21
Khéo léo, nhưng không hoàn toàn ngu ngốc. Hãy thử với{ "tune": "NaNaNaNaNaNaNaNa BATMAN", "score": NaN }
JJJ

1
và bạn phải sử dụng jQuery. Tôi không có $ .get ().
Jason S
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.