字典格式化工具

一、工具简介

        🔍 沐歌字典格式化工具 - 您的数据转换与美化专家

        一款基于 Python Flask 开发的在线工具,专为开发者、数据分析师和测试工程师设计,提供 JSON ↔ Python字典 的双向转换、格式化、压缩、查询等功能,让数据处理更高效、更直观!

        ✨ 核心功能亮点
        ✅ 智能识别:自动检测输入数据是 JSON 还是 Python字典,无需手动切换格式
        ✅ 极速转换:一键完成 JSON ↔ Python字典 互转,支持格式化、压缩
        ✅ 精准查询:内置 JSONPath 查询引擎,快速提取复杂结构数据
        ✅ 代码执行:安全执行 Python表达式,动态处理数据
        ✅ 语法高亮:美观的代码渲染,提升可读性

        🎯 适用场景
        开发者:调试API返回的JSON数据,转换为Python字典
        测试工程师:验证数据结构,提取关键字段
        数据分析师:清洗和预处理JSON数据集
        教学演示:直观展示JSON与Python字典的对应关系

        📌 无需安装,打开即用!
        无论是日常开发还是临时调试,这款工具都能让您的数据处理事半功倍!

二、功能描述

  • ✨这款工具主要提供以下核心功能:
功能类别 具体操作 说明
格式化 json格式化 将紧凑JSON转换为易读格式
格式化 python字典格式化 美化Python字典结构
转换 JSON ↔ Python字典 双向数据格式转换
压缩 数据压缩 去除所有空白字符
查询 JSONPath查询 使用JSONPath表达式提取数据
执行 Python代码执行 对输入数据执行Python表达式

三、技术架构

3.1、前端实现

3.1.1、数据展示区域

  • 输入框支持多行编辑
  • 输出区域带语法高亮
  • 响应式布局适应不同屏幕

3.1.2、用户交互

  • 一键复制功能
  • 邮箱联系方式快速复制
  • 操作结果即时反馈

3.1.3、视觉设计

  • 简洁明了的按钮分组
  • 操作状态颜色区分
  • 友好的错误提示

3.1.4、前端代码

<!DOCTYPE html>
<html>
<head>
    <title>沐歌字典格式化工具</title>
    <style>
        body { 
            font-family: Arial, sans-serif; 
            margin: 20px; 
            line-height: 1.6;
            padding: 0 15px;
        }
        .container { 
            max-width: 1200px; 
            margin: 0 auto; 
        }
        .editor-section {
            margin-bottom: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 15px;
            background: #f9f9f9;
            position: relative;
        }
        h3 {
            margin-top: 0;
            color: #333;
        }
        textarea { 
            width: 100%; 
            height: 200px; 
            font-family: monospace; 
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            resize: vertical;
            box-sizing: border-box;
        }
        .button-row {
            display: flex;
            gap: 10px;
            margin: 15px 0;
            flex-wrap: wrap;
            align-items: center;
        }
        button {
            padding: 8px 15px;
            cursor: pointer;
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            transition: background 0.3s;
            white-space: nowrap;
        }
        button:hover {
            background: #45a049;
        }
        button.secondary {
            background: #2196F3;
        }
        button.secondary:hover {
            background: #0b7dda;
        }
        button.execute {
            background: #ff9800;
        }
        button.execute:hover {
            background: #e68a00;
        }
        .result-container { 
            margin-top: 20px; 
        }
        .error { 
            color: red; 
            margin: 15px 0;
            padding: 10px;
            background: #ffeeee;
            border-radius: 4px;
        }
        .info { 
            color: #31708f; 
            margin: 15px 0;
            padding: 10px;
            background: #d9edf7;
            border-radius: 4px;
        }
        .output-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        .copy-btn {
            background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>') no-repeat center;
            background-size: 20px;
            width: 36px;
            height: 36px;
            border: 1px solid #ddd;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        .copy-btn:hover {
            background-color: #e0e0e0;
        }
        .contact-btn {
            position: absolute;
            top: 15px;
            right: 15px;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background-color: #2196F3;
            border: none;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        .contact-btn:hover {
            background-color: #0b7dda;
        }
        .jsonpath-input {
            flex-grow: 1;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            min-width: 200px;
        }
        .execute-input {
            flex-grow: 1;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            min-width: 200px;
        }
        {{ pygments_css }}
    </style>
</head>
<body>
    <div class="container">
        <h1 style="text-align: center;">沐歌字典格式化工具</h1>

        <form method="post">
            <!-- 输入区域 -->
            <div class="editor-section">
                <h3>输入:</h3>
                <textarea name="dict_data" placeholder="输入Python字典或JSON数据,例如:\n\nDict:\n{'字符': 'string', '真': True, '假': False, '空': None}\n\nJSON:\n{'字符': 'string', '真': true, '假': false, '空': null}">{{ input_data }}</textarea>
                <button type="button" class="contact-btn" onclick="copyEmail()">👤</button>
            </div>

            <!-- 操作按钮 -->
            <div class="button-row">
                <button type="submit" name="action" value="format_json">JSON 格式化</button>
                <button type="submit" name="action" value="format_python">Python 格式化</button>
                <button type="submit" name="action" value="minify">压缩</button>
                <button type="submit" name="action" value="json_to_dict" class="secondary">JSON → Python字典</button>
                <button type="submit" name="action" value="dict_to_json" class="secondary">Python字典 → JSON</button>
            </div>

            <!-- JSONPath查询 -->
            <div class="button-row">
                <input type="text" name="jsonpath_query" class="jsonpath-input" 
                       placeholder="输入JSONPath表达式 (如: $.key, $[*].name)" value="{{ jsonpath_query }}">
                <button type="submit" name="action" value="query_jsonpath" class="execute">执行查询</button>
            </div>

            <!-- 代码执行 -->
            <div class="button-row">
                <input type="text" name="execute_expr" class="execute-input" 
                       placeholder="输入Python表达式 (如: data.get('key'))" value="{{ execute_expr }}">
                <button type="submit" name="action" value="execute_code" class="execute">执行代码</button>
            </div>

            <!-- 输出区域 -->
            <div class="editor-section">
                <div class="output-header">
                    <h3>输出:</h3>
                    <button type="button" onclick="copyToClipboard()" class="copy-btn" title="复制"></button>
                </div>
                <textarea name="result_data" id="result_data">{{ result }}</textarea>
            </div>
        </form>

        <!-- 格式化预览 -->
        {% if formatted_result %}
        <div class="result-container">
            <h3>格式化预览:</h3>
            <div id="formatted-result">{{ formatted_result|safe }}</div>
        </div>
        {% endif %}

        {% if error %}
        <div class="error">{{ error }}</div>
        {% endif %}

        {% if info %}
        <div class="info">{{ info }}</div>
        {% endif %}
    </div>

    <script>
    function copyToClipboard() {
        const result = document.getElementById('result_data');
        result.select();
        result.setSelectionRange(0, 99999);

        try {
            const successful = document.execCommand('copy');
            const msg = successful ? '已复制到剪贴板!' : '复制失败';
            alert(msg);
        } catch (err) {
            try {
                navigator.clipboard.writeText(result.value).then(() => {
                    alert('已复制到剪贴板!');
                }).catch(err => {
                    alert('复制失败,请手动选择文本后按Ctrl+C复制');
                });
            } catch (e) {
                alert('复制失败,请手动选择文本后按Ctrl+C复制');
            }
        }
    }

    function copyEmail() {
        const email = '229421064@qq.com';
        const textarea = document.createElement('textarea');
        textarea.value = email;
        document.body.appendChild(textarea);
        textarea.select();

        try {
            const successful = document.execCommand('copy');
            if (successful) {
                alert('已复制邮箱: ' + email);
            } else {
                throw new Error('Copy command failed');
            }
        } catch (err) {
            try {
                navigator.clipboard.writeText(email).then(() => {
                    alert('已复制邮箱: ' + email);
                }).catch(err => {
                    alert('复制失败,请手动复制邮箱: ' + email);
                });
            } catch (e) {
                alert('复制失败,请手动复制邮箱: ' + email);
            }
        }
        document.body.removeChild(textarea);
    }
    </script>
</body>
</html>

3.2、后端实现

3.2.1、关键函数说明

  • 数据格式化函数
def dict_to_pretty(data, indent=4):
    """递归格式化Python字典"""
    if isinstance(data, dict):
        items = []
        for key, value in data.items():
            items.append(f'{" "*indent}{repr(key)}: {dict_to_pretty(value, indent)}')
        return "{\n" + ",\n".join(items) + "\n}"
    elif isinstance(data, list):
        items = [dict_to_pretty(item, indent) for item in data]
        return "[\n" + ",\n".join(items) + "\n]"
    else:
        return repr(data)
  • 输入类型检测
def detect_input_type(input_str):
    """自动检测输入是JSON还是Python字典"""
    try:
        json.loads(input_str)
        return 'json'
    except ValueError:
        try:
            parsed = ast.literal_eval(input_str)
            if isinstance(parsed, (dict, list)):
                return 'dict'
        except (ValueError, SyntaxError):
            pass
    return None

3.2.2、后端代码

#!/usr/bin/python
# -*- coding: utf-8 -*-
from flask import Flask, render_template_string, request
import json
from pygments import highlight
from pygments.lexers import PythonLexer, JsonLexer
from pygments.formatters import HtmlFormatter
import ast
from collections import OrderedDict
from jsonpath import jsonpath  # 使用jsonpath库
import traceback

app = Flask(__name__)

HTML_TEMPLATE = "html代码"

def dict_to_compact(data):
    """将字典转换为紧凑的Python格式字符串"""
    if isinstance(data, dict):
        items = []
        for key, value in data.items():
            key_str = repr(key)
            value_str = dict_to_compact(value)
            items.append(f"{key_str}:{value_str}")
        return "{" + ",".join(items) + "}"
    elif isinstance(data, list):
        items = [dict_to_compact(item) for item in data]
        return "[" + ",".join(items) + "]"
    else:
        return repr(data)


def dict_to_pretty(data, indent=4):
    """将字典转换为美观的Python格式字符串"""
    if isinstance(data, dict):
        items = []
        for key, value in data.items():
            key_str = repr(key)
            value_str = dict_to_pretty(value, indent)
            items.append(f'{" " * indent}{key_str}: {value_str}')
        return "{\n" + ",\n".join(items) + "\n}"
    elif isinstance(data, list):
        items = []
        for item in data:
            items.append(f'{" " * indent}{dict_to_pretty(item, indent)}')
        return "[\n" + ",\n".join(items) + "\n]"
    else:
        return repr(data)


def detect_input_type(input_str):
    """检测输入数据类型"""
    input_str = input_str.strip()
    if not input_str:
        return None

    # 尝试解析为JSON
    try:
        json.loads(input_str)
        return 'json'
    except ValueError:
        pass

    # 尝试解析为Python字典
    try:
        parsed = ast.literal_eval(input_str)
        if isinstance(parsed, (dict, list)):
            return 'dict'
    except (ValueError, SyntaxError):
        pass

    return None


def parse_input(input_str, expected_type=None):
    """解析输入数据并验证类型"""
    input_str = input_str.strip()
    if not input_str:
        return None, None

    detected_type = detect_input_type(input_str)
    if not detected_type:
        return None, None

    try:
        if detected_type == 'json':
            data = json.loads(input_str, object_pairs_hook=OrderedDict)
        elif detected_type == 'dict':
            data = ast.literal_eval(input_str)
            if isinstance(data, dict):
                data = OrderedDict(data)
            elif not isinstance(data, list):
                return None, None
        else:
            return None, None

        if expected_type and detected_type != expected_type:
            return None, None

        return data, detected_type
    except Exception:
        return None, None


def minify_data(data, data_type):
    """压缩数据"""
    if data_type == 'json':
        return json.dumps(data, separators=(',', ':'), ensure_ascii=False)
    elif data_type == 'dict':
        return dict_to_compact(data)
    else:
        return None


@app.route('/', methods=['GET', 'POST'])
def index():
    input_data = ""
    result = ""
    error = ""
    info = ""
    formatted_result = ""
    jsonpath_query = ""
    execute_expr = ""
    pygments_css = HtmlFormatter().get_style_defs('.highlight')

    if request.method == 'POST':
        input_data = request.form.get('dict_data', '')
        action = request.form.get('action', '')
        jsonpath_query = request.form.get('jsonpath_query', '')
        execute_expr = request.form.get('execute_expr', '')

        try:
            if not input_data.strip():
                raise ValueError("请输入你要操作的数据")

            if action == 'format_json':
                data, data_type = parse_input(input_data, 'json')
                if data is None:
                    raise ValueError("JSON格式化需要JSON格式输入")
                result = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=False)
                formatted_result = highlight_code(result, 'json')

            elif action == 'format_python':
                data, data_type = parse_input(input_data, 'dict')
                if data is None:
                    raise ValueError("Python格式化需要Python字典格式输入")
                result = dict_to_pretty(data, indent=4)
                formatted_result = highlight_code(result, 'python')

            elif action == 'query_jsonpath':
                data, data_type = parse_input(input_data)
                if data is None:
                    raise ValueError("无法识别输入数据格式")

                if not jsonpath_query:
                    raise ValueError("请输入JSONPath查询表达式")

                try:
                    query_result = jsonpath(data, jsonpath_query)
                    if query_result is False:  # jsonpath库在未找到时返回False
                        info = f"JSONPath未找到匹配项: {jsonpath_query}"
                    else:
                        result = dict_to_pretty(query_result, indent=4)
                        formatted_result = f"<h4>JSONPath查询结果 ({jsonpath_query}):</h4>{highlight_code(result, 'python')}"
                        info = f"JSONPath查询成功: {jsonpath_query}"
                except Exception as e:
                    error = f"JSONPath查询错误: {str(e)}"

            elif action == 'execute_code':
                data, data_type = parse_input(input_data)
                if data is None:
                    raise ValueError("无法识别输入数据格式")

                if not execute_expr:
                    raise ValueError("请输入要执行的Python表达式")

                try:
                    # 安全地执行代码,只允许访问data变量
                    locals_dict = {'data': data}
                    globals_dict = {}
                    exec(f"result_value = {execute_expr}", globals_dict, locals_dict)
                    result_value = locals_dict.get('result_value')

                    if isinstance(result_value, (dict, list)):
                        result = dict_to_pretty(result_value, indent=4)
                    else:
                        result = repr(result_value)

                    formatted_result = f"<h4>代码执行结果:</h4>{highlight_code(result, 'python')}"
                    info = f"代码执行成功: {execute_expr}"
                except Exception as e:
                    error = f"代码执行错误: {str(e)}"
                    formatted_result = f"<div class='error'>{error}</div>"

            elif action == 'minify':
                output_data = request.form.get('result_data', '').strip()
                if output_data:
                    output_data, output_type = parse_input(output_data)
                    if output_data is None:
                        raise ValueError("无法压缩输出内容,请先执行格式化操作")
                    result = minify_data(output_data, output_type)
                else:
                    data, data_type = parse_input(input_data)
                    if data is None:
                        raise ValueError("无法识别输入数据格式")
                    result = minify_data(data, data_type)

                if result is None:
                    raise ValueError("不支持压缩此类型数据")

                formatted_result = highlight_code(result, 'json' if (
                                                                        output_type if output_data else data_type) == 'json' else 'python')

            elif action == 'json_to_dict':
                data, data_type = parse_input(input_data, 'json')
                if data is None:
                    raise ValueError("JSON转Python字典需要JSON格式输入")
                result = dict_to_pretty(data, indent=4)
                formatted_result = highlight_code(result, 'python')
                info = "JSON → Python字典 转换完成"

            elif action == 'dict_to_json':
                data, data_type = parse_input(input_data, 'dict')
                if data is None:
                    raise ValueError("Python字典转JSON需要Python字典格式输入")
                result = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=False)
                formatted_result = highlight_code(result, 'json')
                info = "Python字典 → JSON 转换完成"

            else:
                raise ValueError("无效的操作类型")

        except ValueError as e:
            error = str(e)
            result = input_data
        except Exception as e:
            error = f"处理错误: {str(e)}"
            result = input_data
            traceback.print_exc()

    return render_template_string(
        HTML_TEMPLATE,
        input_data=input_data,
        result=result,
        formatted_result=formatted_result,
        error=error,
        info=info,
        jsonpath_query=jsonpath_query,
        execute_expr=execute_expr,
        pygments_css=pygments_css
    )


def highlight_code(code, format_type):
    """高亮显示代码"""
    if format_type == 'json':
        lexer = JsonLexer()
    else:
        lexer = PythonLexer()

    formatter = HtmlFormatter(style='colorful', noclasses=True)
    return highlight(code, lexer, formatter)


if __name__ == '__main__':
    app.run(debug=True, port=9999, host="0.0.0.0")
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容