From 52660afcbaf283da2d5fd3a0d7857ac855f2343a Mon Sep 17 00:00:00 2001 From: Minhyeok Park Date: Thu, 16 Jan 2025 14:33:41 +0900 Subject: [PATCH] feat: add Python dictionary to JSON transforms and remove GzipCompressTransform - Introduced new transforms for converting Python dictionaries to JSON and vice versa in `PythonTransforms.ts`. - Updated `README.md` to reflect the new transformations. - Removed the `GzipCompressTransform.ts` file and integrated its functionality into `GzipTransform.ts`. --- README.md | 2 + src/Transforms/GzipCompressTransform.ts | 82 ------------------------- src/Transforms/GzipTransform.ts | 62 +++++++++++++++++++ src/Transforms/PythonTransforms.ts | 53 ++++++++++++++++ src/Transforms/Transform.ts | 12 +++- 5 files changed, 127 insertions(+), 84 deletions(-) delete mode 100644 src/Transforms/GzipCompressTransform.ts create mode 100644 src/Transforms/PythonTransforms.ts diff --git a/README.md b/README.md index 9f31abb..5e93da1 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,5 @@ A collection of tools for string conversations * String Escaper/Unescaper * JSON2YAML / YAML2JSON * GZIP Zipped Base64 Unzipper +* Python Dictionary to JSON +* JSON to Python Dictionary diff --git a/src/Transforms/GzipCompressTransform.ts b/src/Transforms/GzipCompressTransform.ts deleted file mode 100644 index 4220599..0000000 --- a/src/Transforms/GzipCompressTransform.ts +++ /dev/null @@ -1,82 +0,0 @@ -// GzipCompressTransform.ts -import { Transform } from './Transform' - -/** - * Compresses a string using Gzip and encodes the result in Base64. - * @param input - The input string to compress. - * @returns A promise that resolves to the Base64-encoded compressed string. - */ -const compressGzip = async (input: string): Promise => { - // Check if CompressionStream is supported - if (typeof CompressionStream === 'undefined') { - throw new Error( - 'CompressionStream API is not supported in this environment.' - ) - } - - // Encode the input string to a Uint8Array - const encoder = new TextEncoder() - const encoded = encoder.encode(input) - - // Create a ReadableStream from the encoded data - const readable = new ReadableStream({ - start(controller) { - controller.enqueue(encoded) - controller.close() - } - }) - - // Create a CompressionStream for Gzip - const compressionStream = new CompressionStream('gzip') - - // Pipe the readable stream through the compression stream - const compressedStream = readable.pipeThrough(compressionStream) - - // Collect the compressed chunks - const reader = compressedStream.getReader() - const chunks: Uint8Array[] = [] - let done = false - - while (!done) { - const { value, done: doneReading } = await reader.read() - if (value) { - chunks.push(value) - } - done = doneReading - } - - // Concatenate all chunks into a single Uint8Array - const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0) - const compressedData = new Uint8Array(totalLength) - let offset = 0 - for (const chunk of chunks) { - compressedData.set(chunk, offset) - offset += chunk.length - } - - // Convert the compressed data to a Base64 string - const base64String = arrayBufferToBase64(compressedData) - - return base64String -} - -/** - * Converts a Uint8Array to a Base64-encoded string. - * @param buffer - The Uint8Array to convert. - * @returns The Base64-encoded string. - */ -const arrayBufferToBase64 = (buffer: Uint8Array): string => { - let binary = '' - for (let i = 0; i < buffer.length; i++) { - binary += String.fromCharCode(buffer[i]) - } - return btoa(binary) -} - -export const GzipCompressTransform: Transform = { - name: 'gzipc', // Unique identifier for the transform - - fn: (v: string) => compressGzip(v), - - options: [] // Add any options if needed -} diff --git a/src/Transforms/GzipTransform.ts b/src/Transforms/GzipTransform.ts index 6842bed..1ea1459 100644 --- a/src/Transforms/GzipTransform.ts +++ b/src/Transforms/GzipTransform.ts @@ -19,3 +19,65 @@ export const GzipDecompressTransform: Transform = { fn: (v) => decompressGzip(v) } + +const compressGzip = async (input: string): Promise => { + if (typeof CompressionStream === 'undefined') { + throw new Error( + 'CompressionStream API is not supported in this environment.' + ) + } + + const encoder = new TextEncoder() + const encoded = encoder.encode(input) + + const readable = new ReadableStream({ + start(controller) { + controller.enqueue(encoded) + controller.close() + } + }) + + const compressionStream = new CompressionStream('gzip') + + const compressedStream = readable.pipeThrough(compressionStream) + + const reader = compressedStream.getReader() + const chunks: Uint8Array[] = [] + let done = false + + while (!done) { + const { value, done: doneReading } = await reader.read() + if (value) { + chunks.push(value) + } + done = doneReading + } + + const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0) + const compressedData = new Uint8Array(totalLength) + let offset = 0 + for (const chunk of chunks) { + compressedData.set(chunk, offset) + offset += chunk.length + } + + const base64String = arrayBufferToBase64(compressedData) + + return base64String +} + +const arrayBufferToBase64 = (buffer: Uint8Array): string => { + let binary = '' + for (let i = 0; i < buffer.length; i++) { + binary += String.fromCharCode(buffer[i]) + } + return btoa(binary) +} + +export const GzipCompressTransform: Transform = { + name: 'gzipc', // Unique identifier for the transform + + fn: (v: string) => compressGzip(v), + + options: [] // Add any options if needed +} diff --git a/src/Transforms/PythonTransforms.ts b/src/Transforms/PythonTransforms.ts new file mode 100644 index 0000000..85ffc42 --- /dev/null +++ b/src/Transforms/PythonTransforms.ts @@ -0,0 +1,53 @@ +import { Transform } from './Transform' + +const pythonToJSON = (pythonStr: string): string => { + // Validate basic structure first before any replacements + if (!(/^\s*(?:\{[\s\S]*\}|\[[\s\S]*\])\s*$/.test(pythonStr))) { + throw new Error('Invalid Python dictionary or array format') + } + + // Perform all replacements in a single pass + return pythonStr + .replace(/('|None\b|True\b|False\b)/g, match => { + switch (match) { + case "'": return '"' + case 'None': return 'null' + case 'True': return 'true' + case 'False': return 'false' + default: return match + } + }) +} + +export const PythonDictToJSONTransform: Transform = { + name: 'py2json', + fn: async (v) => { + try { + // Validate the result is actually valid JSON + return JSON.stringify(JSON.parse(pythonToJSON(v))) + } catch { + throw new Error('Invalid Python dictionary or array format') + } + } +} + +export const JSONToPythonDictTransform: Transform = { + name: 'json2py', + fn: async (v) => { + try { + // Validate and format in one step + return JSON.stringify(JSON.parse(v), null, 2) + .replace(/("|\bnull\b|\btrue\b|\bfalse\b)/g, match => { + switch (match) { + case '"': return "'" + case 'null': return 'None' + case 'true': return 'True' + case 'false': return 'False' + default: return match + } + }) + } catch { + throw new Error('Invalid JSON format') + } + } +} diff --git a/src/Transforms/Transform.ts b/src/Transforms/Transform.ts index 36f41b4..503ab9c 100644 --- a/src/Transforms/Transform.ts +++ b/src/Transforms/Transform.ts @@ -3,8 +3,10 @@ import { Base64EncodeTransform } from './Base64Transforms' import { DatetimeTransform } from './DatetimeTransforms' -import { GzipCompressTransform } from './GzipCompressTransform' -import { GzipDecompressTransform } from './GzipTransform' +import { + GzipCompressTransform, + GzipDecompressTransform +} from './GzipTransform' import { JSONBeautifyTransform, JSONEscapeTransform, @@ -14,6 +16,10 @@ import { import { RegexpTransform } from './RegexpTransform' import { URIDecodeTransform, URIEncodeTransform } from './URITransforms' import { JSON2YAMLTransform, YAML2JSONTransform } from './YAMLTransforms' +import { + PythonDictToJSONTransform, + JSONToPythonDictTransform +} from './PythonTransforms' export interface TransformCheckboxOption { type: 'CHECKBOX' @@ -103,6 +109,8 @@ export const transforms: Transform[] = [ JSONUnescapeTransform, JSON2YAMLTransform, YAML2JSONTransform, + PythonDictToJSONTransform, + JSONToPythonDictTransform, GzipCompressTransform, GzipDecompressTransform ]