डेवलपर टूल · JSON फ़ॉर्मेटर और सत्यापनकर्ता
JSON में बड़े पूर्णांक ID: क्यों JavaScript फ़ॉर्मेटर उन्हें गोल कर सकते हैं
· यह क्यों मायने रखती है
json डेवलपर-वर्कफ़्लो मान्यकरण
JSON किसी भी आकार के पूर्णांकों की अनुमति देता है, लेकिन JavaScript संख्याओं को 64-बिट फ़्लोट के रूप में दर्शाता है, इसलिए 2^53 से ऊपर की कोई भी चीज़ पार्स और पुन: क्रमबद्ध होने पर बदल सकती है। यह पोस्ट सीमा बताती है, क्षति का पता कैसे लगाएं, और ID की सुरक्षा कैसे करें।
वह ID जो एक से बदल गई
जो ID एक से बदल जाती है वह अक्सर पूरी तरह से वैध JSON के रूप में आती है। `{"orderId":9007199254740993}` को JavaScript में डालें और `JSON.parse` एक संख्या लौटाता है जिसका प्रदर्शित मान `9007199254740992` है। पार्सिंग सफल होती है क्योंकि टोकन JSON संख्या व्याकरण का पालन करता है; क्षति उन दशमलव अंकों को JavaScript के संख्यात्मक प्रतिनिधित्व में परिवर्तित करते समय होती है। एक फ़ॉर्मेटर जो पार्स किए गए मान को क्रमबद्ध करता है वह ईमानदारी से गोलाकार संख्या लिखता है, न कि स्रोत में दिखाई देने वाला सटीक टोकन।
जब समान अंक उद्धृत किए जाते हैं तो विरोधाभास तत्काल होता है। `JSON.parse("{"orderId":"9007199254740993"}")` स्ट्रिंग `9007199254740993` लौटाता है, प्रत्येक वर्ण को संरक्षित करता है, और `JSON.stringify` उन अंकों को उद्धरण चिह्नों के अंदर अपरिवर्तित छोड़ देता है। यही कारण है कि सिंटैक्स सत्यापन अकेले संख्यात्मक पहचानकर्ता की सुरक्षा नहीं कर सकता है। जब भी लंबे पूर्णांक दिखाई दें तो इनपुट और आउटपुट की तुलना करें, और जब अंकगणित उनके अर्थ का हिस्सा नहीं है तो पहचानकर्ताओं को उत्पादक सीमा पर स्ट्रिंग के रूप में मानें।
RFC 8259 संख्याओं के बारे में क्या कहता है
RFC 8259 JSON संख्या की वर्तनी को परिभाषित करता है लेकिन प्रत्येक कार्यान्वयन को एक मनमाना-सटीक संख्यात्मक प्रकार नहीं देता है। व्याकरण एक वैकल्पिक ऋण चिह्न, एक पूर्णांक भाग, और वैकल्पिक भिन्न और घातांक भाग की अनुमति देता है। इसमें हेक्साडेसिमल नोटेशन, `NaN` और `Infinity` जैसी सुविधाएं शामिल नहीं हैं। नतीजतन, `9007199254740993` वाक्यात्मक रूप से मान्य है, भले ही एक सामान्य JavaScript उपभोक्ता उस पूर्णांक को एक संख्या के रूप में प्रस्तुत नहीं कर सकता है।
विनिर्देश का इंटरऑपरेबिलिटी मार्गदर्शन व्यावहारिक चेतावनी है: सॉफ़्टवेयर आमतौर पर IEEE 754 बाइनरी64 संख्याओं का उपयोग करता है, और नकारात्मक `2^53 + 1` से सकारात्मक `2^53 - 1` तक की सीमा में पूर्णांक सटीक समझौते के अर्थ में इंटरऑपरेबल होते हैं। एक सत्यापनकर्ता एक बड़े टोकन को सही ढंग से स्वीकार कर सकता है जबकि एक पार्सर बाद में इसे राउंड करता है।
2^53 कहां से आता है
`2^53` सीमा बाइनरी64 महत्व में उपलब्ध परिशुद्धता से आती है। JavaScript उच्चतम लगातार प्रतिनिधित्व योग्य पूर्णांक को `Number.MAX_SAFE_INTEGER` के रूप में प्रदर्शित करता है, जो `9007199254740991` है। उस परिमाण पर और उसके नीचे, आसन्न पूर्णांकों को स्पष्ट रूप से दर्शाया जा सकता है। इसके ऊपर, प्रतिनिधित्व योग्य मानों के बीच अंतर बढ़ता है, इसलिए कुछ पड़ोसी दशमलव पूर्णांक उसी संख्या पर मैप होते हैं। रनटाइम किसी स्ट्रिंग को छोटा नहीं कर रहा है; यह उस परिमित बाइनरी प्रारूप में उपलब्ध निकटतम मान का चयन कर रहा है।
एक खुलासा कंसोल चेक `Number.isSafeInteger(9007199254740993)` है, जो गलत है, हालांकि फ़ंक्शन प्राप्त होने से पहले ही स्रोत शाब्दिक को गोल कर दिया गया है। दूसरा है `9007199254740992 === 9007199254740993`, जो JavaScript में सत्य का मूल्यांकन करता है। ये उदाहरण सटीक पूर्णांक पहचान से संबंधित हैं, न कि यह कि क्या प्रत्येक बड़ी संख्या अनुपयोगी हो जाती है।
कैसे पार्स-एंड-रिसेरियलाइज़ अंक खो देता है
पार्स-एंड-रिसेरियलाइज़ फ़ॉर्मेटिंग के तीन चरण होते हैं: संख्यात्मक वर्ण पढ़ें, इन-मेमोरी मान बनाएं, फिर उस मान से ताज़ा वर्ण उत्पन्न करें। मध्य चरण में शाब्दिक विवरण गायब हो जाते हैं। `{"ticket":9223372036854775807}` के साथ, `JSON.parse` निकटतम उपलब्ध JavaScript नंबर बनाता है; `JSON.stringify` फिर `9223372036854776000` उत्सर्जित करता है। सीरिएलाइज़र स्वतंत्र रूप से संरक्षित टोकन को दूषित नहीं कर रहा है। क्रमांकन समय के अनुसार, अंकों का मूल अनुक्रम अब पार्स किए गए ऑब्जेक्ट में मौजूद नहीं है।
ToolAcre का रिपॉजिटरी कार्यान्वयन `JSON.parse` और `JSON.stringify` का उपयोग करता है, इसलिए यह सीमा इसके स्वरूपित आउटपुट पर लागू होती है। इसका सिंटैक्स स्कैनर पार्सिंग विफल होने के बाद एक स्थिर कारण और स्थान प्रदान करने के लिए चलता है; यह JavaScript संख्याओं को मनमाने-सटीक प्रतिनिधित्व से प्रतिस्थापित नहीं करता है। इसलिए एक सफल सत्यापन परिणाम व्याकरण स्थापित करता है, जबकि एक स्वरूपण अंतर सटीक हानि प्रकट कर सकता है।
कार्यान्वित उदाहरण: इनपुट और आउटपुट की तुलना करना
JavaScript राउंड ट्रिप से पहले और बाद में `{"numeric":9007199254740993,"text":"9007199254740993"}` की तुलना करें। `JSON.stringify(JSON.parse(source), null, 2)` चलाने से एक स्वरूपित ऑब्जेक्ट उत्पन्न होता है जिसका `numeric` सदस्य `9007199254740992` होता है, जबकि `text` `"9007199254740993"` रहता है। दोनों सदस्य इनपुट में मान्य थे, और दोनों आउटपुट में वैध रहते हैं। केवल उद्धृत प्रतिनिधित्व ही पहचानकर्ता को सुरक्षित रखता है क्योंकि इसे संख्या के बजाय वर्ण डेटा के रूप में डिकोड किया जाता है।
एक उपयोगी समीक्षा केवल यह नहीं पूछती कि फ़ॉर्मेटर ने हरा दिखाया या नहीं। निर्बाध अंक अनुक्रमों के लिए स्रोत खोजें, सुरक्षित सीमा से अधिक लंबे किसी भी मान की तुलना करें, और निर्धारित करें कि क्या प्रत्येक फ़ील्ड एक मात्रा या एक अपारदर्शी लेबल का प्रतिनिधित्व करता है। यदि निर्माता अनुबंध को नियंत्रित करता है, तो लेबल को वहां एक स्ट्रिंग में बदलें और उपभोक्ताओं के लिए उस विकल्प का दस्तावेजीकरण करें।
स्रोत पर ID की सुरक्षा करना
स्कीमा में स्ट्रिंग्स के रूप में परिभाषित करके और किसी भी JavaScript क्लाइंट को पेलोड प्राप्त होने से पहले उन्हें स्ट्रिंग्स के रूप में क्रमबद्ध करके स्रोत पर ID को सुरक्षित रखें। एक ID में केवल अंक हो सकते हैं और फिर भी अर्थ में गैर-संख्यात्मक हो सकते हैं: परिमाण के आधार पर जोड़, पूर्णांकन और क्रम देना खाता कुंजी पर वैध संचालन नहीं हैं। एक स्ट्रिंग अग्रणी शून्यों को भी सुरक्षित रखती है, जिसे एक संख्यात्मक प्रतिनिधित्व तब भी त्याग देगा जब उसका परिमाण सुरक्षित सीमा के भीतर हो।
इस तथ्य से क्रॉस-भाषा सुरक्षा का अनुमान न लगाएं कि कोई अन्य रनटाइम एक बड़ा पूर्णांक धारण कर सकता है। पार्सर और लक्ष्य प्रकार अलग-अलग होते हैं, और JavaScript में लिखा गया एक मध्यस्थ बाद की सेवा द्वारा देखे जाने से पहले मान को पूर्णांकित कर सकता है। कुछ विशेष पार्सर्स संख्या टोकन को संरक्षित करते हैं या बड़े पूर्णांकों का निर्माण करते हैं, लेकिन प्रत्येक भागीदार को उस अनुबंध को साझा करना होगा।
इसमें क्या शामिल नहीं है
इसमें दशमलव अंकगणित का व्यापक डिज़ाइन शामिल नहीं है। `0.1` जैसे मानों का अपना बाइनरी फ़्लोटिंग-पॉइंट व्यवहार होता है, और एप्लिकेशन अनुबंध के अनुसार धन को स्केल किए गए पूर्णांक या दशमलव प्रकार की आवश्यकता हो सकती है। न ही प्रत्येक संख्या को उद्धृत करने से स्कीमा में स्वचालित रूप से सुधार होता है। गणना, निर्देशांक और माप अक्सर वैध रूप से संख्यात्मक होते हैं। निर्णय इस बात पर निर्भर करता है कि सटीक दशमलव वर्तनी या सटीक पूर्णांक पहचान प्रत्येक उपभोक्ता के डेटा पथ में जीवित रहनी चाहिए या नहीं।
यह चर्चा यह भी दावा नहीं करती है कि JSON ने स्वयं टोकन को गोल कर दिया है या सभी पार्सर्स JavaScript की तरह व्यवहार करते हैं। ठोस भंडार साक्ष्य संकीर्ण है: यह फ़ॉर्मेटर `JSON.parse` और `JSON.stringify` को कॉल करता है, इसलिए JavaScript संख्या शब्दार्थ यहां गैर-उद्धृत मानों को नियंत्रित करते हैं। एक मनमानी-सटीक JSON लाइब्रेरी अलग-अलग विकल्प चुन सकती है, लेकिन इसे यह परिभाषित करना होगा कि मूल्यों को कैसे उजागर और क्रमबद्ध किया जाता है।
टेकअवे: 2^53 से ऊपर की संख्याएँ स्ट्रिंग में हैं
टेकअवे विशिष्ट है: JavaScript की सुरक्षित सीमा के बाहर पूर्णांक पहचानकर्ता स्ट्रिंग में होते हैं जब उन्हें बिना बदले JavaScript से गुजरना पड़ता है। JSON संख्या के रूप में `9007199254740993` वैध सिंटैक्स है लेकिन `JSON.parse` के बाद `9007199254740992` हो जाता है; `"9007199254740993"` सटीक रहता है। उद्धरण सजावट नहीं हैं. वे एक प्रतिनिधित्व का चयन करते हैं जो अंकों को डेटा के रूप में संरक्षित करता है और उपभोक्ताओं को एक अपारदर्शी लेबल को अनुमानित मात्रा के रूप में मानने से रोकता है।
किसी दस्तावेज़ को फ़ॉर्मेटर आउटपुट से बदलने से पहले, मूल संख्याओं से लंबी संख्याओं की तुलना करें और प्रत्येक बदले हुए अंक की जांच करें। जब संभव हो तो निर्माता और स्कीमा को ठीक करें ताकि सभी डाउनस्ट्रीम क्लाइंट को लगातार सुरक्षित फॉर्म प्राप्त हो। ToolAcre परिणाम को उजागर कर सकता है क्योंकि इसका आउटपुट पार्स किए गए JavaScript मान को दर्शाता है, लेकिन यह पार्सिंग के दौरान पहले से खोए गए अंकों का पुनर्निर्माण नहीं कर सकता है।