2025-07-14 21:54:09 +08:00

207 lines
6.9 KiB
C#

// JSONValidator
using UnityEngine;
using UnityEditor;
using System.Text;
using Leguar.TotalJSON;
namespace Leguar.TotalJSON.Internal {
public class JSONValidator : EditorWindow {
private string editorAreaText="Write or copy & paste JSON object or JSON array to this text area. Example JSON:\n\n{\"name\":\"Player\",\"lastLogin\":123456789012,\"achievements\":[42,1337,1703],\"imageUrl\":null,\"have_bought\":true,\"levels\":[{\"passed\":true,\"score\":12345},{\"passed\":false}]}\n\nExtra texts like this before or after JSON object will be removed when clicking button below.";
private string message = "";
private string tightJSON = "";
private string escapedJSON = "";
private Vector2 scrollPos;
private readonly float lineHeight=EditorGUIUtility.singleLineHeight;
[MenuItem("Window/Total JSON/JSON Validator")]
static void Init() {
JSONValidator window=(JSONValidator)(GetWindow(typeof(JSONValidator)));
#if UNITY_5 || UNITY_2017
window.titleContent = new GUIContent("JSON Validator");
#else
window.titleContent = new GUIContent("JSON Validator",loadWindowIcon());
#endif
}
#if !(UNITY_5 || UNITY_2017)
internal static Texture2D loadWindowIcon() {
string dirPath = AssetDatabase.GUIDToAssetPath("9f2fdab21a6aeaf41956a4d04ddf4dd2"); // "Assets/TotalJSON/Internal/Editor"
string iconPath = dirPath+"/window-icon.png";
Texture2D icon = (Texture2D)(AssetDatabase.LoadAssetAtPath(iconPath, typeof(Texture2D)));
return icon;
}
#endif
void OnGUI() {
GUILayout.Space(20);
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
EditorGUI.BeginChangeCheck();
editorAreaText = EditorGUILayout.TextArea(editorAreaText,GUILayout.ExpandHeight(true));
bool changes = EditorGUI.EndChangeCheck();
EditorGUILayout.EndScrollView();
if (changes) {
message = "";
tightJSON = "";
escapedJSON = "";
}
GUILayout.Space(10);
if (GUILayout.Button("Trim, validate and prettify above JSON object or array")) {
string trimmedEditorText=editorAreaText.Trim();
if (trimmedEditorText.Length<editorAreaText.Length) {
editorAreaText=trimmedEditorText; // Needed in case of errors, so that line numbers will match
GUIUtility.keyboardControl=0;
GUIUtility.hotControl=0;
EditorUtility.SetDirty(this);
}
if (string.IsNullOrEmpty(trimmedEditorText)) {
message = "Input is empty";
tightJSON = "";
escapedJSON = "";
} else {
object objectOrError = findAndGetJSONOrJArray(trimmedEditorText);
if (objectOrError==null) {
message = "Can't find JSON start from input";
tightJSON = "";
escapedJSON = "";
} else if (objectOrError is string) {
message = "Invalid input: "+((string)(objectOrError));
tightJSON = "";
escapedJSON = "";
} else {
CreateStringSettings prettySettings=new CreateStringSettings() {
HumanReadable=true,
IndentUsingTab=true,
NewLine=CreateStringSettings.NewLineTypes.LF // \r characters (may be part of environment default) seem to be problem in editor textarea, causing invisible linefeeds, so using plain \n
};
if (objectOrError is JSON[]) {
JSON[] jsons = (JSON[])(objectOrError);
if (jsons.Length==1) {
message = "JSON is valid. Top level JSON key/value pair count = "+jsons[0].Count;
editorAreaText = jsons[0].CreateString(prettySettings)+"\n";
tightJSON = jsons[0].CreateString(new CreateStringSettings() { HumanReadable = false });
escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
} else {
message = "JSONs are valid. JSON object count = "+jsons.Length;
editorAreaText = "";
tightJSON = "";
for (int n = 0; n<jsons.Length; n++) {
editorAreaText += jsons[n].CreateString(prettySettings)+"\n";
if (n<jsons.Length-1) {
editorAreaText += '\n';
}
tightJSON += jsons[n].CreateString(new CreateStringSettings() { HumanReadable = false });
}
escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
}
} else {
JArray jArray = (JArray)(objectOrError);
message = "JSON Array is valid. Top level array length = "+jArray.Length;
editorAreaText = jArray.CreateString(prettySettings)+"\n";
tightJSON = jArray.CreateString(new CreateStringSettings() { HumanReadable = false });
escapedJSON = "\""+getEscapedString(tightJSON)+"\"";
}
GUIUtility.keyboardControl=0;
GUIUtility.hotControl=0;
EditorUtility.SetDirty(this);
}
}
}
GUILayout.Space(20);
GUILayout.Label(message, EditorStyles.wordWrappedLabel);
GUILayout.Space(20);
EditorGUI.BeginDisabledGroup(tightJSON.Length==0);
GUILayout.Label("JSON formatted and encoded string ("+tightJSON.Length+" bytes):", EditorStyles.wordWrappedLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.SelectableLabel(tightJSON,EditorStyles.textField,GUILayout.Height(lineHeight+2));
if (GUILayout.Button("Copy", GUILayout.Width(70))) {
GUIUtility.systemCopyBuffer = tightJSON;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(15);
GUILayout.Label("Above string with escapes (to be used for example directly in c# source code):", EditorStyles.wordWrappedLabel);
GUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.SelectableLabel(escapedJSON,EditorStyles.textField,GUILayout.Height(lineHeight+2));
if (GUILayout.Button("Copy", GUILayout.Width(70))) {
GUIUtility.systemCopyBuffer = escapedJSON;
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(20);
EditorGUI.EndDisabledGroup();
}
private object findAndGetJSONOrJArray(string dirtySourceString) {
// Remove any heading "trash", for example in case that JSON is pasted from Unity Console
int jsonStartIndex=dirtySourceString.IndexOf('{');
int jArrayStartIndex=dirtySourceString.IndexOf('[');
// Nothing?
if (jsonStartIndex<0 && jArrayStartIndex<0) {
return null;
}
// Try parse array
if (jArrayStartIndex>=0 && (jArrayStartIndex<jsonStartIndex || jsonStartIndex<0)) {
try {
return JArray.ParseString(dirtySourceString,new ParseStringSettings(){ ParseStartIndex=jArrayStartIndex, AllowNonWhiteCharactersAfterObject=true });
}
catch (ParseException e) {
if (jsonStartIndex<0) {
return e.Message;
}
}
}
// Parse one or more JSONs
try {
return JSON.ParseStringToMultiple(dirtySourceString,new ParseStringSettings(){ ParseStartIndex=jsonStartIndex, AllowNonWhiteCharactersAfterObject=true });
}
catch (ParseException e) {
return e.Message;
}
}
private static string getEscapedString(string source) {
int length=source.Length;
StringBuilder sb=new StringBuilder(length);
for (int n=0; n<length; n++) {
if (source[n]=='"') {
sb.Append('\\');
sb.Append('"');
} else if (source[n]=='\\') {
sb.Append('\\');
sb.Append('\\');
} else {
sb.Append(source[n]);
}
}
return sb.ToString();
}
}
}