Files
Sprout-Farm/Server/JsonEdit/json_editor.py

268 lines
8.5 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify, render_template, send_file
import json
import os
from datetime import datetime
app = Flask(__name__)
class JSONFormatter:
"""JSON格式化工具类"""
@staticmethod
def format_standard(data, indent=2):
"""标准格式化 - 带缩进的可读格式"""
return json.dumps(data, ensure_ascii=False, indent=indent)
@staticmethod
def minify(data):
"""最小化 - 压缩去除空格"""
return json.dumps(data, ensure_ascii=False, separators=(',', ':'))
@staticmethod
def one_line_per_object(data):
"""一行化 - 每个对象/元素占一行"""
if isinstance(data, list):
# 如果是数组,每个元素占一行
lines = ['[']
for i, item in enumerate(data):
comma = ',' if i < len(data) - 1 else ''
lines.append(f' {json.dumps(item, ensure_ascii=False)}{comma}')
lines.append(']')
return '\n'.join(lines)
elif isinstance(data, dict):
# 如果是对象,每个键值对占一行
lines = ['{']
keys = list(data.keys())
for i, key in enumerate(keys):
comma = ',' if i < len(keys) - 1 else ''
value_str = json.dumps(data[key], ensure_ascii=False)
lines.append(f' {json.dumps(key, ensure_ascii=False)}: {value_str}{comma}')
lines.append('}')
return '\n'.join(lines)
else:
# 基本类型直接返回
return json.dumps(data, ensure_ascii=False)
@app.route('/')
def index():
"""主页"""
return render_template('json_editor.html')
@app.route('/api/format', methods=['POST'])
def format_json():
"""JSON格式化API"""
try:
data = request.get_json()
content = data.get('content', '')
format_type = data.get('format_type', 'standard') # standard, minify, oneline
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
# 解析JSON
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 根据类型格式化
formatter = JSONFormatter()
if format_type == 'standard':
formatted = formatter.format_standard(json_data)
message = 'JSON标准格式化完成'
elif format_type == 'minify':
formatted = formatter.minify(json_data)
message = 'JSON最小化完成'
elif format_type == 'oneline':
formatted = formatter.one_line_per_object(json_data)
message = 'JSON一行化格式完成'
else:
return jsonify({'success': False, 'message': '不支持的格式化类型'})
return jsonify({
'success': True,
'message': message,
'formatted': formatted,
'original_length': len(content),
'formatted_length': len(formatted)
})
except Exception as e:
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
@app.route('/api/batch_add', methods=['POST'])
def batch_add_property():
"""批量添加属性API"""
try:
data = request.get_json()
content = data.get('content', '')
key_name = data.get('key_name', '')
key_value = data.get('key_value', '')
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
if not key_name.strip():
return jsonify({'success': False, 'message': '请提供键名'})
# 解析JSON
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 智能解析键值
parsed_value = parse_value(key_value)
# 批量添加属性
count = add_property_to_all_objects(json_data, key_name, parsed_value)
# 格式化输出
formatted = JSONFormatter.format_standard(json_data)
return jsonify({
'success': True,
'message': f'成功为 {count} 个对象添加了属性 "{key_name}": {json.dumps(parsed_value, ensure_ascii=False)}',
'formatted': formatted,
'count': count
})
except Exception as e:
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
def parse_value(value_str):
"""智能解析值的类型"""
if value_str == '':
return ''
# null
if value_str.lower() == 'null':
return None
# boolean
if value_str.lower() == 'true':
return True
if value_str.lower() == 'false':
return False
# number
try:
if '.' in value_str:
return float(value_str)
else:
return int(value_str)
except ValueError:
pass
# JSON object or array
if (value_str.startswith('{') and value_str.endswith('}')) or \
(value_str.startswith('[') and value_str.endswith(']')):
try:
return json.loads(value_str)
except json.JSONDecodeError:
pass
# string
return value_str
def add_property_to_all_objects(obj, key, value):
"""递归为所有对象添加属性"""
count = 0
def traverse(current):
nonlocal count
if isinstance(current, dict):
current[key] = value
count += 1
# 继续递归处理嵌套对象
for val in current.values():
if isinstance(val, (dict, list)) and val != current:
traverse(val)
elif isinstance(current, list):
for item in current:
traverse(item)
traverse(obj)
return count
@app.route('/api/validate', methods=['POST'])
def validate_json():
"""JSON验证API"""
try:
data = request.get_json()
content = data.get('content', '')
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
try:
json_data = json.loads(content)
return jsonify({
'success': True,
'message': 'JSON格式正确 ✓',
'valid': True
})
except json.JSONDecodeError as e:
return jsonify({
'success': False,
'message': f'JSON格式错误: {str(e)}',
'valid': False,
'error': str(e)
})
except Exception as e:
return jsonify({'success': False, 'message': f'验证错误: {str(e)}'})
@app.route('/api/download', methods=['POST'])
def download_json():
"""下载JSON文件"""
try:
data = request.get_json()
content = data.get('content', '')
format_type = data.get('format_type', 'standard')
if not content.strip():
return jsonify({'success': False, 'message': '没有可下载的内容'})
# 验证JSON格式
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 格式化
formatter = JSONFormatter()
if format_type == 'minify':
formatted_content = formatter.minify(json_data)
elif format_type == 'oneline':
formatted_content = formatter.one_line_per_object(json_data)
else:
formatted_content = formatter.format_standard(json_data)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"edited_json_{format_type}_{timestamp}.json"
# 创建临时文件
temp_file = f"temp_{filename}"
with open(temp_file, 'w', encoding='utf-8') as f:
f.write(formatted_content)
return send_file(temp_file, as_attachment=True, download_name=filename)
except Exception as e:
return jsonify({'success': False, 'message': f'下载错误: {str(e)}'})
if __name__ == '__main__':
# 确保templates目录存在
os.makedirs('templates', exist_ok=True)
# 运行应用
app.run(debug=True, host='0.0.0.0', port=5000)