using System; using System.Collections.Generic; using System.IO; using System.Linq; using AibisDream.Utility; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEngine; namespace AibisDream.SystemEditor { public class JsonEditWindow : EditorWindow { // === 布局变量(可调整) === private float _fileListWidth = 220f; private float _keyListWidth = 180f; private bool _isDraggingFileSplitter = false; private bool _isDraggingKeySplitter = false; // === 核心数据 === private string _rootPath = "Assets/StreamingAssets"; private List _jsonFileList = new List(); private string _selectedFile; private JObject _jsonData; private JArray _jsonArray; // Array格式的JSON数据 private bool _isArrayFormat = false; // 标识当前是否为Array格式 private List _firstLevelKeys = new List(); private string _selectedKey; private bool _editModeTree = true; // true=树形编辑, false=文本编辑 // === 状态 === private Vector2 _scrollPosFiles; private Vector2 _scrollPosKeys; private Vector2 _scrollPosEditor; private string _searchString = ""; private string _newFileName = ""; private string _jsonTextContent = ""; private string _validationError = ""; private Dictionary _keyPositions = new Dictionary(); private Dictionary _expandedNodes = new Dictionary(); private bool _showDeleteButton = false; // 是否显示JSON编辑器中的删除按钮(默认隐藏) [MenuItem(AibisEditorMenus.JsonEditor)] public static void ShowWindow() { GetWindow("JSON Editor"); } private void OnEnable() { _rootPath = EditorPrefs.GetString("JsonEditWindow_RootPath", "Assets/StreamingAssets"); _selectedFile = EditorPrefs.GetString("JsonEditWindow_SelectedFile", ""); _showDeleteButton = EditorPrefs.GetBool("JsonEditWindow_ShowDeleteButton", false); _fileListWidth = EditorPrefs.GetFloat("JsonEditWindow_FileListWidth", 220f); _keyListWidth = EditorPrefs.GetFloat("JsonEditWindow_KeyListWidth", 180f); RefreshFileList(); if (!string.IsNullOrEmpty(_selectedFile) && File.Exists(_selectedFile)) { LoadJsonFile(_selectedFile); } } private void OnDisable() { EditorPrefs.SetString("JsonEditWindow_RootPath", _rootPath); EditorPrefs.SetString("JsonEditWindow_SelectedFile", _selectedFile ?? ""); EditorPrefs.SetBool("JsonEditWindow_ShowDeleteButton", _showDeleteButton); EditorPrefs.SetFloat("JsonEditWindow_FileListWidth", _fileListWidth); EditorPrefs.SetFloat("JsonEditWindow_KeyListWidth", _keyListWidth); } private void OnGUI() { DrawTopBar(); EditorGUILayout.BeginHorizontal(); { DrawFileList(); DrawSplitter(ref _fileListWidth, ref _isDraggingFileSplitter); DrawKeyList(); DrawSplitter(ref _keyListWidth, ref _isDraggingKeySplitter); DrawEditor(); } EditorGUILayout.EndHorizontal(); } // ------------------------------------------------------------------------ // UI 绘制函数 // ------------------------------------------------------------------------ private void DrawTopBar() { EditorGUILayout.BeginHorizontal("box", GUILayout.Height(30)); { GUILayout.Label("资源路径:", GUILayout.Width(60)); _rootPath = EditorGUILayout.TextField(_rootPath, GUILayout.Width(200)); if (GUILayout.Button("...", EditorStyles.miniButton, GUILayout.Width(25))) { string path = EditorUtility.OpenFolderPanel("选择根目录", Application.dataPath, ""); if (!string.IsNullOrEmpty(path) && path.StartsWith(Application.dataPath)) { _rootPath = "Assets" + path.Substring(Application.dataPath.Length); RefreshFileList(); } } GUILayout.Space(20); if (GUILayout.Button("刷新", EditorStyles.toolbarButton, GUILayout.Width(60))) { AssetDatabase.Refresh(); RefreshFileList(); } if (GUILayout.Button("验证", EditorStyles.toolbarButton, GUILayout.Width(60))) { ValidateJson(); } if (GUILayout.Button("格式化", EditorStyles.toolbarButton, GUILayout.Width(60))) { FormatJson(); } if (!string.IsNullOrEmpty(_validationError)) { GUI.color = Color.red; GUILayout.Label(_validationError, EditorStyles.miniLabel); GUI.color = Color.white; } GUILayout.FlexibleSpace(); } EditorGUILayout.EndHorizontal(); } private void DrawSplitter(ref float width, ref bool isDragging) { Rect rect = GUILayoutUtility.GetRect(5f, EditorGUIUtility.singleLineHeight, GUILayout.Width(5f), GUILayout.ExpandHeight(true)); // 绘制分割条 Color splitterColor = EditorGUIUtility.isProSkin ? new Color(0.3f, 0.3f, 0.3f) : new Color(0.6f, 0.6f, 0.6f); EditorGUI.DrawRect(rect, splitterColor); // 检测鼠标事件 Event e = Event.current; int controlID = GUIUtility.GetControlID(FocusType.Passive); switch (e.type) { case EventType.MouseDown: if (rect.Contains(e.mousePosition)) { isDragging = true; GUIUtility.hotControl = controlID; e.Use(); } break; case EventType.MouseDrag: if (isDragging && GUIUtility.hotControl == controlID) { float delta = e.delta.x; width = Mathf.Clamp(width + delta, 100f, 500f); e.Use(); Repaint(); } break; case EventType.MouseUp: if (isDragging) { isDragging = false; GUIUtility.hotControl = 0; e.Use(); } break; } // 改变鼠标光标 if (rect.Contains(e.mousePosition) || isDragging) { EditorGUIUtility.AddCursorRect(rect, MouseCursor.ResizeHorizontal); } } private void DrawFileList() { EditorGUILayout.BeginVertical("box", GUILayout.Width(_fileListWidth), GUILayout.ExpandHeight(true)); GUILayout.Label("文件列表", EditorStyles.boldLabel); GUILayout.Space(5); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("搜索:", GUILayout.Width(40)); string newSearch = EditorGUILayout.TextField(_searchString, EditorStyles.toolbarSearchField); if (newSearch != _searchString) { _searchString = newSearch; RefreshFileList(); } } EditorGUILayout.EndHorizontal(); GUILayout.Space(5); EditorGUILayout.BeginHorizontal(); { GUILayout.Label("新文件:", GUILayout.Width(50)); _newFileName = EditorGUILayout.TextField(_newFileName, GUILayout.Width(100)); if (GUILayout.Button("创建", EditorStyles.miniButton, GUILayout.Width(50))) { CreateJsonFile(); } } EditorGUILayout.EndHorizontal(); GUILayout.Space(5); _scrollPosFiles = EditorGUILayout.BeginScrollView(_scrollPosFiles); if (_jsonFileList != null && _jsonFileList.Count > 0) { foreach (var filePath in _jsonFileList) { if (filePath == null) continue; string fileName = Path.GetFileName(filePath); bool isSelected = _selectedFile == filePath; EditorGUILayout.BeginHorizontal("box"); { if (isSelected) GUI.backgroundColor = new Color(0.6f, 0.8f, 1f); if (GUILayout.Button(fileName, EditorStyles.label, GUILayout.Height(22))) { SelectFile(filePath); } GUI.backgroundColor = Color.white; // 定位按钮 if (GUILayout.Button(EditorGUIUtility.IconContent("d_ViewToolOrbit"), EditorStyles.iconButton, GUILayout.Width(22))) { string assetPath = filePath.Replace(Application.dataPath, "Assets").Replace("\\", "/"); var asset = AssetDatabase.LoadAssetAtPath(assetPath); if (asset != null) EditorGUIUtility.PingObject(asset); } // 删除按钮 if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash"), EditorStyles.iconButton, GUILayout.Width(22))) { if (EditorUtility.DisplayDialog("删除确认", $"确定删除 {fileName} 吗?", "删除", "取消")) { DeleteFile(filePath); GUIUtility.ExitGUI(); } } } EditorGUILayout.EndHorizontal(); } } else { GUILayout.Label("没有找到JSON文件", EditorStyles.centeredGreyMiniLabel); } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); } private void DrawKeyList() { EditorGUILayout.BeginVertical("box", GUILayout.Width(_keyListWidth), GUILayout.ExpandHeight(true)); GUILayout.Label("第一层Key", EditorStyles.boldLabel); GUILayout.Space(5); _scrollPosKeys = EditorGUILayout.BeginScrollView(_scrollPosKeys); if (_isArrayFormat) { // Array格式,Key栏置空 GUILayout.FlexibleSpace(); GUILayout.Label("Array格式\n无Key列表", EditorStyles.centeredGreyMiniLabel); GUILayout.FlexibleSpace(); } else if (_jsonData != null && _firstLevelKeys != null && _firstLevelKeys.Count > 0) { foreach (var key in _firstLevelKeys) { bool isSelected = (_selectedKey == key); if (isSelected) GUI.backgroundColor = new Color(0.6f, 0.8f, 1f); string displayText = key; if (_jsonData[key] != null) { JToken token = _jsonData[key]; if (token.Type == JTokenType.Object) displayText = $"{{}} {key}"; else if (token.Type == JTokenType.Array) displayText = $"[] {key}"; else displayText = $"{GetValueTypeIcon(token)} {key}"; } if (GUILayout.Button(displayText, EditorStyles.miniButton, GUILayout.Height(24))) { SelectKey(key); } GUI.backgroundColor = Color.white; } } else { GUILayout.Label("<< 请选择文件", EditorStyles.centeredGreyMiniLabel); } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); } private void DrawEditor() { EditorGUILayout.BeginVertical("box", GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); if (_jsonData != null || _jsonArray != null) { EditorGUILayout.BeginHorizontal(); { GUILayout.Label("JSON编辑器", EditorStyles.boldLabel); if (_isArrayFormat) { GUI.color = new Color(0.6f, 0.8f, 1f); GUILayout.Label("Array格式", EditorStyles.miniLabel); GUI.color = Color.white; } else if (!string.IsNullOrEmpty(_selectedKey)) { GUI.color = new Color(0.6f, 0.8f, 1f); GUILayout.Label($"当前Key: {_selectedKey}", EditorStyles.miniLabel); GUI.color = Color.white; if (GUILayout.Button("显示全部", EditorStyles.miniButton, GUILayout.Width(70))) { _selectedKey = null; } } GUILayout.FlexibleSpace(); _showDeleteButton = GUILayout.Toggle(_showDeleteButton, "显示删除", EditorStyles.toolbarButton, GUILayout.Width(70)); _editModeTree = GUILayout.Toggle(_editModeTree, "树形编辑", EditorStyles.toolbarButton); _editModeTree = !GUILayout.Toggle(!_editModeTree, "文本编辑", EditorStyles.toolbarButton); } EditorGUILayout.EndHorizontal(); GUILayout.Space(5); EditorGUILayout.BeginHorizontal(); { EditorGUI.BeginDisabledGroup(string.IsNullOrEmpty(_selectedFile)); if (GUILayout.Button("保存", EditorStyles.miniButton, GUILayout.Width(60))) { SaveJsonFile(); } EditorGUI.EndDisabledGroup(); EditorGUI.BeginDisabledGroup(string.IsNullOrEmpty(_selectedFile)); if (GUILayout.Button("重新加载", EditorStyles.miniButton, GUILayout.Width(70))) { if (!string.IsNullOrEmpty(_selectedFile)) LoadJsonFile(_selectedFile); } EditorGUI.EndDisabledGroup(); GUILayout.FlexibleSpace(); if (!string.IsNullOrEmpty(_validationError)) { GUI.color = Color.red; GUILayout.Label(_validationError, EditorStyles.miniLabel); GUI.color = Color.white; } } EditorGUILayout.EndHorizontal(); GUILayout.Space(5); _scrollPosEditor = EditorGUILayout.BeginScrollView(_scrollPosEditor); if (_editModeTree) { if (_isArrayFormat) { DrawArrayTreeEditor(); } else { DrawTreeEditor(); } } else { DrawTextEditor(); } EditorGUILayout.EndScrollView(); } else { GUILayout.FlexibleSpace(); GUILayout.Label("<< 请选择JSON文件", EditorStyles.centeredGreyMiniLabel); GUILayout.FlexibleSpace(); } EditorGUILayout.EndVertical(); } private void DrawTreeEditor() { if (_jsonData == null) return; _keyPositions.Clear(); // 如果选中了Key,只显示该Key对应的部分 if (!string.IsNullOrEmpty(_selectedKey) && _jsonData[_selectedKey] != null) { JToken selectedToken = _jsonData[_selectedKey]; DrawJToken(selectedToken, _selectedKey, 0, _selectedKey); } else { // 显示完整的JSON foreach (var prop in _jsonData.Properties()) { DrawJToken(prop.Value, prop.Name, 0, prop.Name); } } } private void DrawArrayTreeEditor() { if (_jsonArray == null) return; EditorGUILayout.BeginHorizontal(); { GUILayout.Space(15); if (GUILayout.Button("+ 添加元素", EditorStyles.miniButton, GUILayout.Width(100))) { _jsonArray.Add(""); } } EditorGUILayout.EndHorizontal(); GUILayout.Space(5); var indicesToRemove = new List(); for (int i = 0; i < _jsonArray.Count; i++) { if (_showDeleteButton) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(15); if (GUILayout.Button("×", EditorStyles.miniButton, GUILayout.Width(20))) { indicesToRemove.Add(i); } EditorGUILayout.EndHorizontal(); } DrawJToken(_jsonArray[i], $"[{i}]", 0, $"[{i}]"); } // 删除元素(从后往前删除,避免索引问题) for (int i = indicesToRemove.Count - 1; i >= 0; i--) { _jsonArray.RemoveAt(indicesToRemove[i]); } } private void DrawJToken(JToken token, string key, int indent, string path = "") { if (token == null) return; string nodePath = string.IsNullOrEmpty(path) ? key : $"{path}.{key}"; float startY = GUILayoutUtility.GetRect(0, 0).y; EditorGUILayout.BeginHorizontal(); GUILayout.Space(indent * 15); if (token.Type == JTokenType.Object) { JObject obj = (JObject)token; if (!_expandedNodes.ContainsKey(nodePath)) _expandedNodes[nodePath] = true; bool expanded = _expandedNodes[nodePath]; expanded = EditorGUILayout.Foldout(expanded, $"{key}: {{}} ({obj.Count})", true); _expandedNodes[nodePath] = expanded; if (GUILayout.Button("+", EditorStyles.miniButtonLeft, GUILayout.Width(20))) { string newKey = "newKey"; obj[newKey] = ""; } EditorGUILayout.EndHorizontal(); if (expanded) { var propsToRemove = new List(); foreach (var prop in obj.Properties().ToList()) { if (_showDeleteButton) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(15); if (GUILayout.Button("×", EditorStyles.miniButton, GUILayout.Width(20))) { propsToRemove.Add(prop.Name); } EditorGUILayout.EndHorizontal(); } DrawJToken(prop.Value, prop.Name, indent + 1, nodePath); } foreach (var keyToRemove in propsToRemove) { obj.Remove(keyToRemove); } } } else if (token.Type == JTokenType.Array) { JArray arr = (JArray)token; if (!_expandedNodes.ContainsKey(nodePath)) _expandedNodes[nodePath] = true; bool expanded = _expandedNodes[nodePath]; expanded = EditorGUILayout.Foldout(expanded, $"{key}: [{arr.Count}]", true); _expandedNodes[nodePath] = expanded; if (GUILayout.Button("+", EditorStyles.miniButtonLeft, GUILayout.Width(20))) { arr.Add(""); } EditorGUILayout.EndHorizontal(); if (expanded) { var indicesToRemove = new List(); for (int i = 0; i < arr.Count; i++) { if (_showDeleteButton) { EditorGUILayout.BeginHorizontal(); GUILayout.Space(15); if (GUILayout.Button("×", EditorStyles.miniButton, GUILayout.Width(20))) { indicesToRemove.Add(i); } EditorGUILayout.EndHorizontal(); } DrawJToken(arr[i], $"[{i}]", indent + 1, nodePath); } for (int i = indicesToRemove.Count - 1; i >= 0; i--) { arr.RemoveAt(indicesToRemove[i]); } } } else { // 值类型 string valueStr = token.ToString(); if (token.Type == JTokenType.String) valueStr = $"\"{valueStr}\""; EditorGUILayout.LabelField($"{key}:", GUILayout.Width(100)); string newValue = EditorGUILayout.TextField(valueStr); if (newValue != valueStr) { try { if (token.Type == JTokenType.String) { string trimmed = newValue.Trim('"'); token.Replace(trimmed); } else if (token.Type == JTokenType.Integer) { if (long.TryParse(newValue, out long longVal)) token.Replace(longVal); } else if (token.Type == JTokenType.Float) { if (double.TryParse(newValue, out double doubleVal)) token.Replace(doubleVal); } else if (token.Type == JTokenType.Boolean) { if (bool.TryParse(newValue, out bool boolVal)) token.Replace(boolVal); } else { token.Replace(newValue); } } catch { } } EditorGUILayout.EndHorizontal(); } if (indent == 0 && !string.IsNullOrEmpty(key)) { float endY = GUILayoutUtility.GetRect(0, 0).y; _keyPositions[key] = startY; } } private void DrawTextEditor() { string displayText = _jsonTextContent; // 如果选中了Key,只显示该Key对应的部分 if (!string.IsNullOrEmpty(_selectedKey) && _jsonData != null && _jsonData[_selectedKey] != null) { JToken selectedToken = _jsonData[_selectedKey]; displayText = selectedToken.ToString(Newtonsoft.Json.Formatting.Indented); } EditorGUI.BeginChangeCheck(); displayText = EditorGUILayout.TextArea(displayText, GUILayout.ExpandHeight(true)); if (EditorGUI.EndChangeCheck()) { try { // 如果只编辑选中的Key部分,需要更新整个JSON if (!string.IsNullOrEmpty(_selectedKey) && _jsonData != null) { JToken newToken = JToken.Parse(displayText); _jsonData[_selectedKey] = newToken; _jsonTextContent = _jsonData.ToString(Newtonsoft.Json.Formatting.Indented); } else if (_isArrayFormat) { // Array格式 _jsonArray = JArray.Parse(displayText); _jsonTextContent = displayText; } else { // Object格式 _jsonData = JObject.Parse(displayText); _jsonTextContent = displayText; _firstLevelKeys = _jsonData.Properties().Select(p => p.Name).ToList(); } _validationError = ""; } catch (Exception e) { _validationError = $"JSON格式错误: {e.Message}"; } } } // ------------------------------------------------------------------------ // 逻辑处理 // ------------------------------------------------------------------------ private void RefreshFileList() { _jsonFileList.Clear(); if (string.IsNullOrEmpty(_rootPath)) return; string fullPath = _rootPath; if (!Path.IsPathRooted(fullPath)) { fullPath = Path.Combine(Application.dataPath.Replace("Assets", ""), _rootPath).Replace("\\", "/"); Debug.Log("fullPath: " + fullPath); } Debug.Log("fullPath: " + Directory.Exists(fullPath)); if (!Directory.Exists(fullPath)) return; string[] files = Directory.GetFiles(fullPath, "*.json", SearchOption.AllDirectories); foreach (var file in files) { string relativePath = file.Replace(Application.dataPath, "Assets"); if (string.IsNullOrEmpty(_searchString) || Path.GetFileName(file).ToLower().Contains(_searchString.ToLower())) { _jsonFileList.Add(file); } } _jsonFileList.Sort(); } private void SelectFile(string filePath) { _selectedFile = filePath; _selectedKey = null; // 切换文件时清除选中的Key,显示完整JSON _isArrayFormat = false; // 重置格式标识 LoadJsonFile(filePath); GUI.FocusControl(null); } private void SelectKey(string key) { _selectedKey = key; // 选中Key时,重置滚动位置到顶部(因为只显示该Key的内容) _scrollPosEditor.y = 0; Repaint(); } private void LoadJsonFile(string filePath) { try { string jsonText = File.ReadAllText(filePath, System.Text.Encoding.UTF8); JToken rootToken = JToken.Parse(jsonText); // 判断是Object还是Array if (rootToken.Type == JTokenType.Array) { _jsonArray = (JArray)rootToken; _jsonData = null; _isArrayFormat = true; _firstLevelKeys.Clear(); _jsonTextContent = _jsonArray.ToString(Newtonsoft.Json.Formatting.Indented); } else if (rootToken.Type == JTokenType.Object) { _jsonData = (JObject)rootToken; _jsonArray = null; _isArrayFormat = false; _firstLevelKeys = _jsonData.Properties().Select(p => p.Name).ToList(); _jsonTextContent = _jsonData.ToString(Newtonsoft.Json.Formatting.Indented); } else { throw new Exception("JSON格式必须是Object或Array"); } _selectedKey = null; _validationError = ""; _expandedNodes.Clear(); _keyPositions.Clear(); } catch (Exception e) { _validationError = $"加载失败: {e.Message}"; _jsonData = null; _jsonArray = null; _isArrayFormat = false; _firstLevelKeys.Clear(); } } private void SaveJsonFile() { if (string.IsNullOrEmpty(_selectedFile)) return; if (_jsonData == null && _jsonArray == null) return; try { string jsonText; if (_isArrayFormat && _jsonArray != null) { jsonText = _jsonArray.ToString(Newtonsoft.Json.Formatting.Indented); } else if (_jsonData != null) { jsonText = _jsonData.ToString(Newtonsoft.Json.Formatting.Indented); } else { return; } File.WriteAllText(_selectedFile, jsonText, System.Text.Encoding.UTF8); _jsonTextContent = jsonText; _validationError = ""; AssetDatabase.Refresh(); EditorUtility.DisplayDialog("保存成功", "JSON文件已保存", "确定"); } catch (Exception e) { _validationError = $"保存失败: {e.Message}"; EditorUtility.DisplayDialog("保存失败", e.Message, "确定"); } } private void CreateJsonFile() { if (string.IsNullOrEmpty(_newFileName)) return; string fullPath = _rootPath; if (!Path.IsPathRooted(fullPath)) { fullPath = Path.Combine(Application.dataPath, _rootPath.Replace("Assets/", "")); } if (!Directory.Exists(fullPath)) { Directory.CreateDirectory(fullPath); } string fileName = JsonUtil.FormatAsJsonPath(_newFileName); string filePath = Path.Combine(fullPath, fileName); filePath = GetUniqueFilePath(filePath); JObject newJson = new JObject(); File.WriteAllText(filePath, newJson.ToString(Newtonsoft.Json.Formatting.Indented), System.Text.Encoding.UTF8); AssetDatabase.Refresh(); RefreshFileList(); SelectFile(filePath); _newFileName = ""; } private void DeleteFile(string filePath) { try { File.Delete(filePath); if (File.Exists(filePath + ".meta")) File.Delete(filePath + ".meta"); AssetDatabase.Refresh(); if (_selectedFile == filePath) { _selectedFile = null; _jsonData = null; _jsonArray = null; _isArrayFormat = false; _firstLevelKeys.Clear(); } RefreshFileList(); } catch (Exception e) { EditorUtility.DisplayDialog("删除失败", e.Message, "确定"); } } private void DuplicateFile(string filePath) { try { string dir = Path.GetDirectoryName(filePath); string fileName = Path.GetFileNameWithoutExtension(filePath); string ext = Path.GetExtension(filePath); string newPath = Path.Combine(dir, fileName + "_copy" + ext); newPath = GetUniqueFilePath(newPath); File.Copy(filePath, newPath); AssetDatabase.Refresh(); RefreshFileList(); } catch (Exception e) { EditorUtility.DisplayDialog("复制失败", e.Message, "确定"); } } private void ValidateJson() { if (string.IsNullOrEmpty(_selectedFile) && _jsonData == null && _jsonArray == null) { // 验证文本编辑器中的内容 if (!string.IsNullOrEmpty(_jsonTextContent)) { try { JToken.Parse(_jsonTextContent); _validationError = ""; EditorUtility.DisplayDialog("验证成功", "JSON格式正确", "确定"); } catch (Exception e) { _validationError = $"验证失败: {e.Message}"; EditorUtility.DisplayDialog("验证失败", e.Message, "确定"); } } return; } try { string jsonText; if (_jsonArray != null) jsonText = _jsonArray.ToString(); else if (_jsonData != null) jsonText = _jsonData.ToString(); else jsonText = File.ReadAllText(_selectedFile, System.Text.Encoding.UTF8); JToken.Parse(jsonText); _validationError = ""; EditorUtility.DisplayDialog("验证成功", "JSON格式正确", "确定"); } catch (Exception e) { _validationError = $"验证失败: {e.Message}"; EditorUtility.DisplayDialog("验证失败", e.Message, "确定"); } } private void FormatJson() { if (_jsonData == null && _jsonArray == null) { // 如果当前是文本模式,尝试从文本格式化 if (!string.IsNullOrEmpty(_jsonTextContent)) { try { JToken rootToken = JToken.Parse(_jsonTextContent); if (rootToken.Type == JTokenType.Array) { _jsonArray = (JArray)rootToken; _jsonData = null; _isArrayFormat = true; _firstLevelKeys.Clear(); } else { _jsonData = (JObject)rootToken; _jsonArray = null; _isArrayFormat = false; _firstLevelKeys = _jsonData.Properties().Select(p => p.Name).ToList(); } string formatted = rootToken.ToString(Newtonsoft.Json.Formatting.Indented); _jsonTextContent = formatted; _validationError = ""; return; } catch (Exception e) { _validationError = $"格式化失败: {e.Message}"; return; } } return; } try { string formatted; if (_isArrayFormat && _jsonArray != null) { formatted = _jsonArray.ToString(Newtonsoft.Json.Formatting.Indented); _jsonArray = JArray.Parse(formatted); } else if (_jsonData != null) { formatted = _jsonData.ToString(Newtonsoft.Json.Formatting.Indented); _jsonData = JObject.Parse(formatted); _firstLevelKeys = _jsonData.Properties().Select(p => p.Name).ToList(); } else { return; } _jsonTextContent = formatted; _validationError = ""; } catch (Exception e) { _validationError = $"格式化失败: {e.Message}"; } } private void RenameFile(string filePath, string newName) { try { string dir = Path.GetDirectoryName(filePath); string ext = Path.GetExtension(filePath); string newPath = Path.Combine(dir, newName + ext); if (File.Exists(newPath)) { EditorUtility.DisplayDialog("重命名失败", "文件已存在", "确定"); return; } File.Move(filePath, newPath); if (File.Exists(filePath + ".meta")) { File.Move(filePath + ".meta", newPath + ".meta"); } AssetDatabase.Refresh(); if (_selectedFile == filePath) { _selectedFile = newPath; } RefreshFileList(); } catch (Exception e) { EditorUtility.DisplayDialog("重命名失败", e.Message, "确定"); } } private string GetUniqueFilePath(string filePath) { if (!File.Exists(filePath)) return filePath; string dir = Path.GetDirectoryName(filePath); string fileName = Path.GetFileNameWithoutExtension(filePath); string ext = Path.GetExtension(filePath); int counter = 1; string newPath; do { newPath = Path.Combine(dir, $"{fileName}_{counter}{ext}"); counter++; } while (File.Exists(newPath)); return newPath; } private string GetValueTypeIcon(JToken token) { switch (token.Type) { case JTokenType.String: return "\"\""; case JTokenType.Integer: return "123"; case JTokenType.Float: return "1.0"; case JTokenType.Boolean: return token.Value() ? "true" : "false"; case JTokenType.Null: return "null"; default: return "?"; } } } }