├── Editor.meta ├── Editor ├── BennyKok.NotionAPI.Editor.asmdef ├── BennyKok.NotionAPI.Editor.asmdef.meta ├── DatabaseSchemaEditor.cs ├── DatabaseSchemaEditor.cs.meta ├── EditorButtonPropertyDrawer.cs ├── EditorButtonPropertyDrawer.cs.meta ├── SimpleJSON.cs └── SimpleJSON.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Runtime.meta ├── Runtime ├── BennyKok.NotionAPI.asmdef ├── BennyKok.NotionAPI.asmdef.meta ├── DataModel.meta ├── DataModel │ ├── Block.cs │ ├── Block.cs.meta │ ├── Common.cs │ ├── Common.cs.meta │ ├── Property.cs │ └── Property.cs.meta ├── DatabaseSchema.cs ├── DatabaseSchema.cs.meta ├── EditorButtonAttribute.cs ├── EditorButtonAttribute.cs.meta ├── NotionAPI.cs ├── NotionAPI.cs.meta ├── NotionAPITest.cs └── NotionAPITest.cs.meta ├── package.json └── package.json.meta /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cd6127108c5d964a906a6743df14d8a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/BennyKok.NotionAPI.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BennyKok.NotionAPI.Editor", 3 | "rootNamespace": "BennyKok.NotionAPI.Editor", 4 | "references": [ 5 | "GUID:9a678814a577206439b7c76fbbddfba3", 6 | "GUID:478a2357cc57436488a56e564b08d223" 7 | ], 8 | "includePlatforms": [ 9 | "Editor" 10 | ], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": false, 13 | "overrideReferences": false, 14 | "precompiledReferences": [], 15 | "autoReferenced": true, 16 | "defineConstraints": [], 17 | "versionDefines": [], 18 | "noEngineReferences": false 19 | } -------------------------------------------------------------------------------- /Editor/BennyKok.NotionAPI.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b8a330a24614284abcbda301a51d33c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/DatabaseSchemaEditor.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using UnityEngine; 4 | using Unity.EditorCoroutines.Editor; 5 | using BennyKok.NotionAPI.Editor.SimpleJSON; 6 | using System.Text.RegularExpressions; 7 | using System; 8 | using UnityEditor; 9 | 10 | namespace BennyKok.NotionAPI.Editor 11 | { 12 | [UnityEditor.CustomEditor(typeof(DatabaseSchema))] 13 | public class DatabaseSchemaEditor : UnityEditor.Editor 14 | { 15 | private DatabaseSchema m_target 16 | { 17 | get { return (DatabaseSchema)target; } 18 | } 19 | 20 | private bool busy; 21 | 22 | public override void OnInspectorGUI() 23 | { 24 | base.OnInspectorGUI(); 25 | 26 | EditorGUI.BeginDisabledGroup(busy || string.IsNullOrEmpty(m_target.apiKey) || string.IsNullOrEmpty(m_target.database_id)); 27 | 28 | if (GUILayout.Button("Fetch Schema")) 29 | { 30 | var api = new NotionAPI(m_target.apiKey); 31 | 32 | busy = true; 33 | 34 | EditorCoroutineUtility.StartCoroutine(api.GetDatabaseJSON(m_target.database_id, (db) => 35 | { 36 | Debug.Log(db); 37 | Undo.RecordObject(m_target, "Update Schema"); 38 | var json = JSON.Parse(db); 39 | m_target.fieldNames.Clear(); 40 | m_target.fieldTypes.Clear(); 41 | foreach (var node in json["properties"]) 42 | { 43 | m_target.fieldNames.Add(node.Key); 44 | m_target.fieldTypes.Add(node.Value["type"]); 45 | } 46 | EditorUtility.SetDirty(m_target); 47 | busy = false; 48 | }), this); 49 | } 50 | 51 | EditorGUI.EndDisabledGroup(); 52 | 53 | EditorGUI.BeginDisabledGroup(busy || m_target.fieldNames == null || m_target.fieldNames.Count == 0); 54 | 55 | if (GUILayout.Button("Create Schema Class")) 56 | { 57 | CreateCodeSchemaFile(m_target); 58 | } 59 | 60 | if(GUILayout.Button("Create Serialized Database Asset")) 61 | { 62 | CreateSerializedDatabaseFile(m_target); 63 | } 64 | 65 | EditorGUI.EndDisabledGroup(); 66 | } 67 | 68 | public void CreateCodeSchemaFile(DatabaseSchema target) 69 | { 70 | var sb = new StringBuilder(); 71 | string className = RemoveWhitespace(target.name); 72 | 73 | sb.Append($"using {typeof(IDObject).Namespace};"); 74 | sb.Append(Environment.NewLine); 75 | sb.Append("using System;"); 76 | sb.Append(Environment.NewLine); 77 | sb.Append(Environment.NewLine); 78 | sb.Append("[Serializable]"); 79 | sb.Append(Environment.NewLine); 80 | sb.Append("public class "); 81 | sb.Append(className); 82 | sb.Append(Environment.NewLine); 83 | sb.Append("{"); 84 | sb.Append(Environment.NewLine); 85 | 86 | for (int i = 0; i < target.fieldNames.Count; i++) 87 | { 88 | var notionType = GetPropertyTypeFromNotionType(target.fieldTypes[i]); 89 | 90 | if (notionType == null) continue; 91 | 92 | var field = target.fieldNames[i]; 93 | sb.Append(" "); 94 | sb.Append("public "); 95 | sb.Append(notionType.Name); 96 | sb.Append(" "); 97 | sb.Append(field); 98 | sb.Append(";"); 99 | sb.Append(Environment.NewLine); 100 | } 101 | 102 | sb.Append("}"); 103 | 104 | var path = Directory.GetParent(AssetDatabase.GetAssetPath(target)); 105 | var scriptPath = Path.Combine(path.FullName, className + ".cs"); 106 | using (var w = File.CreateText(scriptPath)) 107 | { 108 | w.Write(sb); 109 | } 110 | 111 | scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length); 112 | AssetDatabase.ImportAsset(scriptPath); 113 | } 114 | 115 | public void CreateSerializedDatabaseFile(DatabaseSchema target) 116 | { 117 | var sb = new StringBuilder(); 118 | int indentLevel = 0; 119 | string className = RemoveWhitespace(target.name) + "Database"; 120 | 121 | void NewLine() 122 | { 123 | sb.Append(Environment.NewLine); 124 | if (indentLevel > 0) sb.Append(" ".PadLeft(indentLevel * 4)); 125 | } 126 | 127 | sb.Append($"using {typeof(IDObject).Namespace};"); 128 | NewLine(); 129 | sb.Append("using UnityEngine;"); 130 | NewLine(); 131 | sb.Append("#if UNITY_EDITOR"); 132 | NewLine(); 133 | sb.Append("using Unity.EditorCoroutines.Editor;"); 134 | NewLine(); 135 | sb.Append("#endif"); 136 | NewLine(); 137 | NewLine(); 138 | //sb.Append(string.Format("[CreateAssetMenu(fileName = \"{0}\", menuName = \"Notion API/Databases/{1}\")]", className, className)); 139 | //NewLine(); 140 | sb.Append("public class "); 141 | sb.Append(className); 142 | sb.Append(" : ScriptableObject"); 143 | NewLine(); 144 | sb.Append("{"); 145 | 146 | indentLevel++; 147 | 148 | NewLine(); 149 | 150 | //PROPERTIES 151 | sb.Append("[System.Serializable]"); 152 | NewLine(); 153 | sb.Append("public class Definition"); 154 | NewLine(); 155 | sb.Append("{"); 156 | 157 | indentLevel++; 158 | 159 | NewLine(); 160 | 161 | //TITLE 162 | sb.Append("public TitleProperty Name;"); 163 | 164 | for (int i = 0; i < target.fieldNames.Count; i++) 165 | { 166 | var notionType = GetPropertyDefinitionFromNotionType(target.fieldTypes[i]); 167 | 168 | if (notionType == null) continue; 169 | 170 | var field = target.fieldNames[i]; 171 | NewLine(); 172 | sb.Append("public "); 173 | sb.Append(notionType.Name); 174 | sb.Append(" "); 175 | sb.Append(field); 176 | sb.Append(";"); 177 | } 178 | 179 | indentLevel--; 180 | 181 | NewLine(); 182 | sb.Append("}"); 183 | 184 | NewLine(); 185 | NewLine(); 186 | 187 | //PAGES 188 | sb.Append("[System.Serializable]"); 189 | NewLine(); 190 | sb.Append("public class Properties"); 191 | NewLine(); 192 | sb.Append("{"); 193 | 194 | indentLevel++; 195 | 196 | bool hasPeopleProperty = false; 197 | 198 | for (int i = 0; i < target.fieldNames.Count; i++) 199 | { 200 | var notionType = GetPropertyTypeFromNotionType(target.fieldTypes[i]); 201 | 202 | if (notionType == null) continue; 203 | if (notionType == typeof(PeopleProperty)) hasPeopleProperty = true; 204 | 205 | var field = target.fieldNames[i]; 206 | NewLine(); 207 | sb.Append("public "); 208 | sb.Append(notionType.Name); 209 | sb.Append(" "); 210 | sb.Append(field); 211 | sb.Append(";"); 212 | } 213 | 214 | indentLevel--; 215 | 216 | NewLine(); 217 | sb.Append("}"); 218 | 219 | NewLine(); 220 | 221 | NewLine(); 222 | sb.Append("public DatabaseSchema databaseSchema;"); 223 | 224 | NewLine(); 225 | sb.Append("public Database database;"); 226 | 227 | NewLine(); 228 | sb.Append("public Page[] pages;"); 229 | 230 | if (hasPeopleProperty) 231 | { 232 | NewLine(); 233 | sb.Append("public DatabaseUsers users;"); 234 | } 235 | 236 | NewLine(); 237 | NewLine(); 238 | sb.Append("#if UNITY_EDITOR"); 239 | NewLine(); 240 | sb.Append("[EditorButton(\"Fetch Data From Notion\", \"SyncEditor\")]"); 241 | NewLine(); 242 | sb.Append("public bool doSync;"); 243 | NewLine(); 244 | NewLine(); 245 | sb.Append("public void SyncEditor()"); 246 | NewLine(); 247 | sb.Append("{"); 248 | indentLevel++; 249 | NewLine(); 250 | sb.Append("var api = new NotionAPI(databaseSchema.apiKey);"); 251 | NewLine(); 252 | sb.Append("EditorCoroutineUtility.StartCoroutine(api.GetDatabase(databaseSchema.database_id, (db) => { database = db; }), this);"); 253 | NewLine(); 254 | sb.Append("EditorCoroutineUtility.StartCoroutine(api.QueryDatabase(databaseSchema.database_id, (pages) => { this.pages = pages.results; }), this);"); 255 | 256 | if (hasPeopleProperty) 257 | { 258 | NewLine(); 259 | sb.Append("EditorCoroutineUtility.StartCoroutine(api.GetUsers((users) => { this.users = users; }), this);"); 260 | } 261 | 262 | NewLine(); 263 | sb.Append("UnityEditor.EditorUtility.SetDirty(this);"); 264 | 265 | indentLevel--; 266 | NewLine(); 267 | sb.Append("}"); 268 | NewLine(); 269 | sb.Append("#endif"); 270 | 271 | indentLevel--; 272 | NewLine(); 273 | sb.Append("}"); 274 | 275 | 276 | var path = Directory.GetParent(AssetDatabase.GetAssetPath(target)); 277 | var scriptPath = Path.Combine(path.FullName, className + ".cs"); 278 | using (var w = File.CreateText(scriptPath)) 279 | { 280 | w.Write(sb); 281 | } 282 | 283 | scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length); 284 | AssetDatabase.ImportAsset(scriptPath); 285 | 286 | EditorPrefs.SetString("NotionAPI_DatabaseName", className); 287 | EditorPrefs.SetString("NotionAPI_DatabasePath", Path.Combine(path.ToString(), className)); 288 | EditorPrefs.SetInt("NotionAPI_DatabaseSchemaId", target.GetInstanceID()); 289 | } 290 | 291 | public Type GetPropertyTypeFromNotionType(string notionType) 292 | { 293 | switch (notionType) 294 | { 295 | case "number": return typeof(NumberProperty); 296 | case "title": return typeof(TitleProperty); 297 | case "rich_text": return typeof(TextProperty); 298 | case "multi_select": return typeof(MultiSelectProperty); 299 | case "select": return typeof(SelectProperty); 300 | case "checkbox": return typeof(CheckboxProperty); 301 | case "date": return typeof(DateProperty); 302 | case "formula": return typeof(FormulaStringProperty); 303 | case "people": return typeof(PeopleProperty); 304 | } 305 | 306 | return null; 307 | } 308 | 309 | public Type GetPropertyDefinitionFromNotionType(string notionType) 310 | { 311 | switch (notionType) 312 | { 313 | case "multi_select": return typeof(MultiSelectPropertyDefinition); 314 | case "select": return typeof(SelectPropertyDefinition); 315 | } 316 | 317 | return null; 318 | } 319 | 320 | private string RemoveWhitespace(string text) 321 | { 322 | return Regex.Replace(text, @"\s", ""); 323 | } 324 | 325 | [UnityEditor.Callbacks.DidReloadScripts(100)] 326 | private static void OnScriptsReload() 327 | { 328 | if (!EditorPrefs.HasKey("NotionAPI_DatabaseName")) return; 329 | 330 | string className = EditorPrefs.GetString("NotionAPI_DatabaseName"); 331 | string path = EditorPrefs.GetString("NotionAPI_DatabasePath"); 332 | int schemaInstanceId = EditorPrefs.GetInt("NotionAPI_DatabaseSchemaId"); 333 | 334 | EditorPrefs.DeleteKey("NotionAPI_DatabaseName"); 335 | EditorPrefs.DeleteKey("NotionAPI_DatabasePath"); 336 | EditorPrefs.DeleteKey("NotionAPI_DatabaseSchemaId"); 337 | 338 | var schema = EditorUtility.InstanceIDToObject(schemaInstanceId); 339 | 340 | ScriptableObject so = CreateInstance(className); 341 | so.name = className; 342 | so.GetType().GetField("databaseSchema").SetValue(so,schema); 343 | 344 | AssetDatabase.CreateAsset(so, path + ".asset"); 345 | AssetDatabase.SaveAssets(); 346 | } 347 | 348 | // Escape regex https://www.freeformatter.com/java-dotnet-escape.html#ad-output 349 | 350 | // https://stackoverflow.com/a/60556735 351 | // https://stackoverflow.com/a/35129815 352 | 353 | // Regex extractPropertiesGroupRgx = new Regex("(?:\\\"|\\')(?:properties)(?:\\\"|\\')(?=:)(?:\\:\\s*)(?:\\\"|\\')?(\\{(?:(?>[^{}\"'\\/]+)|(?>\"(?:(?>[^\\\\\"]+)|\\\\.)*\")|(?>'(?:(?>[^\\\\']+)|\\\\.)*')|(?>\\/\\/.*\\n)|(?>\\/\\*.*?\\*\\/)|(?-1))*\\})", RegexOptions.Multiline); 354 | // Regex extractPropertiesRgx = new Regex("(?:\\\"|\\')(?:properties)(?:\\\"|\\')(?=:)(?:\\:\\s*)(?:\\\"|\\')?(\\{(?:(?>[^{}\"'\\/]+)|(?>\"(?:(?>[^\\\\\"]+)|\\\\.)*\")|(?>'(?:(?>[^\\\\']+)|\\\\.)*')|(?>\\/\\/.*\\n)|(?>\\/\\*.*?\\*\\/)|(?-1))*\\})", RegexOptions.Multiline); 355 | 356 | // public string ExtractPropertiesGroupJSON(string rawDbJSON) 357 | // { 358 | // var match = extractPropertiesGroupRgx.Match(rawDbJSON); 359 | // Debug.Log(match.Groups[0].Value); 360 | // return match.Groups[0].Value; 361 | // } 362 | 363 | // public void ExtractPropertiesJSON(string rawDbJSON) 364 | // { 365 | // var match = extractPropertiesGroupRgx.Match(rawDbJSON); 366 | // } 367 | } 368 | } -------------------------------------------------------------------------------- /Editor/DatabaseSchemaEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5237ee747cfc94c419de53150fa17955 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/EditorButtonPropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Reflection; 4 | 5 | namespace BennyKok.NotionAPI.Editor 6 | { 7 | [CustomPropertyDrawer(typeof(EditorButtonAttribute))] 8 | public class EditorButtonPropertyDrawer : PropertyDrawer 9 | { 10 | private MethodInfo _eventMethodInfo = null; 11 | 12 | public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) 13 | { 14 | EditorButtonAttribute button = (EditorButtonAttribute)attribute; 15 | 16 | if (GUI.Button(rect, button.label)) 17 | { 18 | System.Type type = prop.serializedObject.targetObject.GetType(); 19 | 20 | if (_eventMethodInfo == null) 21 | _eventMethodInfo = type.GetMethod(button.methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 22 | 23 | if (_eventMethodInfo != null) 24 | _eventMethodInfo.Invoke(prop.serializedObject.targetObject, null); 25 | else 26 | Debug.LogWarning(string.Format("Unable to find method {0} in {1}", button.methodName, type)); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Editor/EditorButtonPropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e80fdc666f235c42a37c95a7412d669 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/SimpleJSON.cs: -------------------------------------------------------------------------------- 1 | /* * * * * 2 | * A simple JSON Parser / builder 3 | * ------------------------------ 4 | * 5 | * It mainly has been written as a simple JSON parser. It can build a JSON string 6 | * from the node-tree, or generate a node tree from any valid JSON string. 7 | * 8 | * Written by Bunny83 9 | * 2012-06-09 10 | * 11 | * Changelog now external. See Changelog.txt 12 | * 13 | * The MIT License (MIT) 14 | * 15 | * Copyright (c) 2012-2019 Markus Göbel (Bunny83) 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a copy 18 | * of this software and associated documentation files (the "Software"), to deal 19 | * in the Software without restriction, including without limitation the rights 20 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | * copies of the Software, and to permit persons to whom the Software is 22 | * furnished to do so, subject to the following conditions: 23 | * 24 | * The above copyright notice and this permission notice shall be included in all 25 | * copies or substantial portions of the Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | * SOFTWARE. 34 | * 35 | * * * * */ 36 | using System; 37 | using System.Collections; 38 | using System.Collections.Generic; 39 | using System.Globalization; 40 | using System.Linq; 41 | using System.Text; 42 | 43 | namespace BennyKok.NotionAPI.Editor.SimpleJSON 44 | { 45 | public enum JSONNodeType 46 | { 47 | Array = 1, 48 | Object = 2, 49 | String = 3, 50 | Number = 4, 51 | NullValue = 5, 52 | Boolean = 6, 53 | None = 7, 54 | Custom = 0xFF, 55 | } 56 | public enum JSONTextMode 57 | { 58 | Compact, 59 | Indent 60 | } 61 | 62 | public abstract partial class JSONNode 63 | { 64 | #region Enumerators 65 | public struct Enumerator 66 | { 67 | private enum Type { None, Array, Object } 68 | private Type type; 69 | private Dictionary.Enumerator m_Object; 70 | private List.Enumerator m_Array; 71 | public bool IsValid { get { return type != Type.None; } } 72 | public Enumerator(List.Enumerator aArrayEnum) 73 | { 74 | type = Type.Array; 75 | m_Object = default(Dictionary.Enumerator); 76 | m_Array = aArrayEnum; 77 | } 78 | public Enumerator(Dictionary.Enumerator aDictEnum) 79 | { 80 | type = Type.Object; 81 | m_Object = aDictEnum; 82 | m_Array = default(List.Enumerator); 83 | } 84 | public KeyValuePair Current 85 | { 86 | get 87 | { 88 | if (type == Type.Array) 89 | return new KeyValuePair(string.Empty, m_Array.Current); 90 | else if (type == Type.Object) 91 | return m_Object.Current; 92 | return new KeyValuePair(string.Empty, null); 93 | } 94 | } 95 | public bool MoveNext() 96 | { 97 | if (type == Type.Array) 98 | return m_Array.MoveNext(); 99 | else if (type == Type.Object) 100 | return m_Object.MoveNext(); 101 | return false; 102 | } 103 | } 104 | public struct ValueEnumerator 105 | { 106 | private Enumerator m_Enumerator; 107 | public ValueEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } 108 | public ValueEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } 109 | public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } 110 | public JSONNode Current { get { return m_Enumerator.Current.Value; } } 111 | public bool MoveNext() { return m_Enumerator.MoveNext(); } 112 | public ValueEnumerator GetEnumerator() { return this; } 113 | } 114 | public struct KeyEnumerator 115 | { 116 | private Enumerator m_Enumerator; 117 | public KeyEnumerator(List.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } 118 | public KeyEnumerator(Dictionary.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } 119 | public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } 120 | public string Current { get { return m_Enumerator.Current.Key; } } 121 | public bool MoveNext() { return m_Enumerator.MoveNext(); } 122 | public KeyEnumerator GetEnumerator() { return this; } 123 | } 124 | 125 | public class LinqEnumerator : IEnumerator>, IEnumerable> 126 | { 127 | private JSONNode m_Node; 128 | private Enumerator m_Enumerator; 129 | internal LinqEnumerator(JSONNode aNode) 130 | { 131 | m_Node = aNode; 132 | if (m_Node != null) 133 | m_Enumerator = m_Node.GetEnumerator(); 134 | } 135 | public KeyValuePair Current { get { return m_Enumerator.Current; } } 136 | object IEnumerator.Current { get { return m_Enumerator.Current; } } 137 | public bool MoveNext() { return m_Enumerator.MoveNext(); } 138 | 139 | public void Dispose() 140 | { 141 | m_Node = null; 142 | m_Enumerator = new Enumerator(); 143 | } 144 | 145 | public IEnumerator> GetEnumerator() 146 | { 147 | return new LinqEnumerator(m_Node); 148 | } 149 | 150 | public void Reset() 151 | { 152 | if (m_Node != null) 153 | m_Enumerator = m_Node.GetEnumerator(); 154 | } 155 | 156 | IEnumerator IEnumerable.GetEnumerator() 157 | { 158 | return new LinqEnumerator(m_Node); 159 | } 160 | } 161 | 162 | #endregion Enumerators 163 | 164 | #region common interface 165 | 166 | public static bool forceASCII = false; // Use Unicode by default 167 | public static bool longAsString = false; // lazy creator creates a JSONString instead of JSONNumber 168 | public static bool allowLineComments = true; // allow "//"-style comments at the end of a line 169 | 170 | public abstract JSONNodeType Tag { get; } 171 | 172 | public virtual JSONNode this[int aIndex] { get { return null; } set { } } 173 | 174 | public virtual JSONNode this[string aKey] { get { return null; } set { } } 175 | 176 | public virtual string Value { get { return ""; } set { } } 177 | 178 | public virtual int Count { get { return 0; } } 179 | 180 | public virtual bool IsNumber { get { return false; } } 181 | public virtual bool IsString { get { return false; } } 182 | public virtual bool IsBoolean { get { return false; } } 183 | public virtual bool IsNull { get { return false; } } 184 | public virtual bool IsArray { get { return false; } } 185 | public virtual bool IsObject { get { return false; } } 186 | 187 | public virtual bool Inline { get { return false; } set { } } 188 | 189 | public virtual void Add(string aKey, JSONNode aItem) 190 | { 191 | } 192 | public virtual void Add(JSONNode aItem) 193 | { 194 | Add("", aItem); 195 | } 196 | 197 | public virtual JSONNode Remove(string aKey) 198 | { 199 | return null; 200 | } 201 | 202 | public virtual JSONNode Remove(int aIndex) 203 | { 204 | return null; 205 | } 206 | 207 | public virtual JSONNode Remove(JSONNode aNode) 208 | { 209 | return aNode; 210 | } 211 | public virtual void Clear() { } 212 | 213 | public virtual JSONNode Clone() 214 | { 215 | return null; 216 | } 217 | 218 | public virtual IEnumerable Children 219 | { 220 | get 221 | { 222 | yield break; 223 | } 224 | } 225 | 226 | public IEnumerable DeepChildren 227 | { 228 | get 229 | { 230 | foreach (var C in Children) 231 | foreach (var D in C.DeepChildren) 232 | yield return D; 233 | } 234 | } 235 | 236 | public virtual bool HasKey(string aKey) 237 | { 238 | return false; 239 | } 240 | 241 | public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) 242 | { 243 | return aDefault; 244 | } 245 | 246 | public override string ToString() 247 | { 248 | StringBuilder sb = new StringBuilder(); 249 | WriteToStringBuilder(sb, 0, 0, JSONTextMode.Compact); 250 | return sb.ToString(); 251 | } 252 | 253 | public virtual string ToString(int aIndent) 254 | { 255 | StringBuilder sb = new StringBuilder(); 256 | WriteToStringBuilder(sb, 0, aIndent, JSONTextMode.Indent); 257 | return sb.ToString(); 258 | } 259 | internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); 260 | 261 | public abstract Enumerator GetEnumerator(); 262 | public IEnumerable> Linq { get { return new LinqEnumerator(this); } } 263 | public KeyEnumerator Keys { get { return new KeyEnumerator(GetEnumerator()); } } 264 | public ValueEnumerator Values { get { return new ValueEnumerator(GetEnumerator()); } } 265 | 266 | #endregion common interface 267 | 268 | #region typecasting properties 269 | 270 | 271 | public virtual double AsDouble 272 | { 273 | get 274 | { 275 | double v = 0.0; 276 | if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) 277 | return v; 278 | return 0.0; 279 | } 280 | set 281 | { 282 | Value = value.ToString(CultureInfo.InvariantCulture); 283 | } 284 | } 285 | 286 | public virtual int AsInt 287 | { 288 | get { return (int)AsDouble; } 289 | set { AsDouble = value; } 290 | } 291 | 292 | public virtual float AsFloat 293 | { 294 | get { return (float)AsDouble; } 295 | set { AsDouble = value; } 296 | } 297 | 298 | public virtual bool AsBool 299 | { 300 | get 301 | { 302 | bool v = false; 303 | if (bool.TryParse(Value, out v)) 304 | return v; 305 | return !string.IsNullOrEmpty(Value); 306 | } 307 | set 308 | { 309 | Value = (value) ? "true" : "false"; 310 | } 311 | } 312 | 313 | public virtual long AsLong 314 | { 315 | get 316 | { 317 | long val = 0; 318 | if (long.TryParse(Value, out val)) 319 | return val; 320 | return 0L; 321 | } 322 | set 323 | { 324 | Value = value.ToString(); 325 | } 326 | } 327 | 328 | public virtual ulong AsULong 329 | { 330 | get 331 | { 332 | ulong val = 0; 333 | if (ulong.TryParse(Value, out val)) 334 | return val; 335 | return 0; 336 | } 337 | set 338 | { 339 | Value = value.ToString(); 340 | } 341 | } 342 | 343 | public virtual JSONArray AsArray 344 | { 345 | get 346 | { 347 | return this as JSONArray; 348 | } 349 | } 350 | 351 | public virtual JSONObject AsObject 352 | { 353 | get 354 | { 355 | return this as JSONObject; 356 | } 357 | } 358 | 359 | 360 | #endregion typecasting properties 361 | 362 | #region operators 363 | 364 | public static implicit operator JSONNode(string s) 365 | { 366 | return (s == null) ? (JSONNode) JSONNull.CreateOrGet() : new JSONString(s); 367 | } 368 | public static implicit operator string(JSONNode d) 369 | { 370 | return (d == null) ? null : d.Value; 371 | } 372 | 373 | public static implicit operator JSONNode(double n) 374 | { 375 | return new JSONNumber(n); 376 | } 377 | public static implicit operator double(JSONNode d) 378 | { 379 | return (d == null) ? 0 : d.AsDouble; 380 | } 381 | 382 | public static implicit operator JSONNode(float n) 383 | { 384 | return new JSONNumber(n); 385 | } 386 | public static implicit operator float(JSONNode d) 387 | { 388 | return (d == null) ? 0 : d.AsFloat; 389 | } 390 | 391 | public static implicit operator JSONNode(int n) 392 | { 393 | return new JSONNumber(n); 394 | } 395 | public static implicit operator int(JSONNode d) 396 | { 397 | return (d == null) ? 0 : d.AsInt; 398 | } 399 | 400 | public static implicit operator JSONNode(long n) 401 | { 402 | if (longAsString) 403 | return new JSONString(n.ToString()); 404 | return new JSONNumber(n); 405 | } 406 | public static implicit operator long(JSONNode d) 407 | { 408 | return (d == null) ? 0L : d.AsLong; 409 | } 410 | 411 | public static implicit operator JSONNode(ulong n) 412 | { 413 | if (longAsString) 414 | return new JSONString(n.ToString()); 415 | return new JSONNumber(n); 416 | } 417 | public static implicit operator ulong(JSONNode d) 418 | { 419 | return (d == null) ? 0 : d.AsULong; 420 | } 421 | 422 | public static implicit operator JSONNode(bool b) 423 | { 424 | return new JSONBool(b); 425 | } 426 | public static implicit operator bool(JSONNode d) 427 | { 428 | return (d == null) ? false : d.AsBool; 429 | } 430 | 431 | public static implicit operator JSONNode(KeyValuePair aKeyValue) 432 | { 433 | return aKeyValue.Value; 434 | } 435 | 436 | public static bool operator ==(JSONNode a, object b) 437 | { 438 | if (ReferenceEquals(a, b)) 439 | return true; 440 | bool aIsNull = a is JSONNull || ReferenceEquals(a, null) || a is JSONLazyCreator; 441 | bool bIsNull = b is JSONNull || ReferenceEquals(b, null) || b is JSONLazyCreator; 442 | if (aIsNull && bIsNull) 443 | return true; 444 | return !aIsNull && a.Equals(b); 445 | } 446 | 447 | public static bool operator !=(JSONNode a, object b) 448 | { 449 | return !(a == b); 450 | } 451 | 452 | public override bool Equals(object obj) 453 | { 454 | return ReferenceEquals(this, obj); 455 | } 456 | 457 | public override int GetHashCode() 458 | { 459 | return base.GetHashCode(); 460 | } 461 | 462 | #endregion operators 463 | 464 | [ThreadStatic] 465 | private static StringBuilder m_EscapeBuilder; 466 | internal static StringBuilder EscapeBuilder 467 | { 468 | get 469 | { 470 | if (m_EscapeBuilder == null) 471 | m_EscapeBuilder = new StringBuilder(); 472 | return m_EscapeBuilder; 473 | } 474 | } 475 | internal static string Escape(string aText) 476 | { 477 | var sb = EscapeBuilder; 478 | sb.Length = 0; 479 | if (sb.Capacity < aText.Length + aText.Length / 10) 480 | sb.Capacity = aText.Length + aText.Length / 10; 481 | foreach (char c in aText) 482 | { 483 | switch (c) 484 | { 485 | case '\\': 486 | sb.Append("\\\\"); 487 | break; 488 | case '\"': 489 | sb.Append("\\\""); 490 | break; 491 | case '\n': 492 | sb.Append("\\n"); 493 | break; 494 | case '\r': 495 | sb.Append("\\r"); 496 | break; 497 | case '\t': 498 | sb.Append("\\t"); 499 | break; 500 | case '\b': 501 | sb.Append("\\b"); 502 | break; 503 | case '\f': 504 | sb.Append("\\f"); 505 | break; 506 | default: 507 | if (c < ' ' || (forceASCII && c > 127)) 508 | { 509 | ushort val = c; 510 | sb.Append("\\u").Append(val.ToString("X4")); 511 | } 512 | else 513 | sb.Append(c); 514 | break; 515 | } 516 | } 517 | string result = sb.ToString(); 518 | sb.Length = 0; 519 | return result; 520 | } 521 | 522 | private static JSONNode ParseElement(string token, bool quoted) 523 | { 524 | if (quoted) 525 | return token; 526 | if (token.Length <= 5) 527 | { 528 | string tmp = token.ToLower(); 529 | if (tmp == "false" || tmp == "true") 530 | return tmp == "true"; 531 | if (tmp == "null") 532 | return JSONNull.CreateOrGet(); 533 | } 534 | double val; 535 | if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) 536 | return val; 537 | else 538 | return token; 539 | } 540 | 541 | public static JSONNode Parse(string aJSON) 542 | { 543 | Stack stack = new Stack(); 544 | JSONNode ctx = null; 545 | int i = 0; 546 | StringBuilder Token = new StringBuilder(); 547 | string TokenName = ""; 548 | bool QuoteMode = false; 549 | bool TokenIsQuoted = false; 550 | bool HasNewlineChar = false; 551 | while (i < aJSON.Length) 552 | { 553 | switch (aJSON[i]) 554 | { 555 | case '{': 556 | if (QuoteMode) 557 | { 558 | Token.Append(aJSON[i]); 559 | break; 560 | } 561 | stack.Push(new JSONObject()); 562 | if (ctx != null) 563 | { 564 | ctx.Add(TokenName, stack.Peek()); 565 | } 566 | TokenName = ""; 567 | Token.Length = 0; 568 | ctx = stack.Peek(); 569 | HasNewlineChar = false; 570 | break; 571 | 572 | case '[': 573 | if (QuoteMode) 574 | { 575 | Token.Append(aJSON[i]); 576 | break; 577 | } 578 | 579 | stack.Push(new JSONArray()); 580 | if (ctx != null) 581 | { 582 | ctx.Add(TokenName, stack.Peek()); 583 | } 584 | TokenName = ""; 585 | Token.Length = 0; 586 | ctx = stack.Peek(); 587 | HasNewlineChar = false; 588 | break; 589 | 590 | case '}': 591 | case ']': 592 | if (QuoteMode) 593 | { 594 | 595 | Token.Append(aJSON[i]); 596 | break; 597 | } 598 | if (stack.Count == 0) 599 | throw new Exception("JSON Parse: Too many closing brackets"); 600 | 601 | stack.Pop(); 602 | if (Token.Length > 0 || TokenIsQuoted) 603 | ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); 604 | if (ctx != null) 605 | ctx.Inline = !HasNewlineChar; 606 | TokenIsQuoted = false; 607 | TokenName = ""; 608 | Token.Length = 0; 609 | if (stack.Count > 0) 610 | ctx = stack.Peek(); 611 | break; 612 | 613 | case ':': 614 | if (QuoteMode) 615 | { 616 | Token.Append(aJSON[i]); 617 | break; 618 | } 619 | TokenName = Token.ToString(); 620 | Token.Length = 0; 621 | TokenIsQuoted = false; 622 | break; 623 | 624 | case '"': 625 | QuoteMode ^= true; 626 | TokenIsQuoted |= QuoteMode; 627 | break; 628 | 629 | case ',': 630 | if (QuoteMode) 631 | { 632 | Token.Append(aJSON[i]); 633 | break; 634 | } 635 | if (Token.Length > 0 || TokenIsQuoted) 636 | ctx.Add(TokenName, ParseElement(Token.ToString(), TokenIsQuoted)); 637 | TokenIsQuoted = false; 638 | TokenName = ""; 639 | Token.Length = 0; 640 | TokenIsQuoted = false; 641 | break; 642 | 643 | case '\r': 644 | case '\n': 645 | HasNewlineChar = true; 646 | break; 647 | 648 | case ' ': 649 | case '\t': 650 | if (QuoteMode) 651 | Token.Append(aJSON[i]); 652 | break; 653 | 654 | case '\\': 655 | ++i; 656 | if (QuoteMode) 657 | { 658 | char C = aJSON[i]; 659 | switch (C) 660 | { 661 | case 't': 662 | Token.Append('\t'); 663 | break; 664 | case 'r': 665 | Token.Append('\r'); 666 | break; 667 | case 'n': 668 | Token.Append('\n'); 669 | break; 670 | case 'b': 671 | Token.Append('\b'); 672 | break; 673 | case 'f': 674 | Token.Append('\f'); 675 | break; 676 | case 'u': 677 | { 678 | string s = aJSON.Substring(i + 1, 4); 679 | Token.Append((char)int.Parse( 680 | s, 681 | System.Globalization.NumberStyles.AllowHexSpecifier)); 682 | i += 4; 683 | break; 684 | } 685 | default: 686 | Token.Append(C); 687 | break; 688 | } 689 | } 690 | break; 691 | case '/': 692 | if (allowLineComments && !QuoteMode && i + 1 < aJSON.Length && aJSON[i + 1] == '/') 693 | { 694 | while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') ; 695 | break; 696 | } 697 | Token.Append(aJSON[i]); 698 | break; 699 | case '\uFEFF': // remove / ignore BOM (Byte Order Mark) 700 | break; 701 | 702 | default: 703 | Token.Append(aJSON[i]); 704 | break; 705 | } 706 | ++i; 707 | } 708 | if (QuoteMode) 709 | { 710 | throw new Exception("JSON Parse: Quotation marks seems to be messed up."); 711 | } 712 | if (ctx == null) 713 | return ParseElement(Token.ToString(), TokenIsQuoted); 714 | return ctx; 715 | } 716 | 717 | } 718 | // End of JSONNode 719 | 720 | public partial class JSONArray : JSONNode 721 | { 722 | private List m_List = new List(); 723 | private bool inline = false; 724 | public override bool Inline 725 | { 726 | get { return inline; } 727 | set { inline = value; } 728 | } 729 | 730 | public override JSONNodeType Tag { get { return JSONNodeType.Array; } } 731 | public override bool IsArray { get { return true; } } 732 | public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } 733 | 734 | public override JSONNode this[int aIndex] 735 | { 736 | get 737 | { 738 | if (aIndex < 0 || aIndex >= m_List.Count) 739 | return new JSONLazyCreator(this); 740 | return m_List[aIndex]; 741 | } 742 | set 743 | { 744 | if (value == null) 745 | value = JSONNull.CreateOrGet(); 746 | if (aIndex < 0 || aIndex >= m_List.Count) 747 | m_List.Add(value); 748 | else 749 | m_List[aIndex] = value; 750 | } 751 | } 752 | 753 | public override JSONNode this[string aKey] 754 | { 755 | get { return new JSONLazyCreator(this); } 756 | set 757 | { 758 | if (value == null) 759 | value = JSONNull.CreateOrGet(); 760 | m_List.Add(value); 761 | } 762 | } 763 | 764 | public override int Count 765 | { 766 | get { return m_List.Count; } 767 | } 768 | 769 | public override void Add(string aKey, JSONNode aItem) 770 | { 771 | if (aItem == null) 772 | aItem = JSONNull.CreateOrGet(); 773 | m_List.Add(aItem); 774 | } 775 | 776 | public override JSONNode Remove(int aIndex) 777 | { 778 | if (aIndex < 0 || aIndex >= m_List.Count) 779 | return null; 780 | JSONNode tmp = m_List[aIndex]; 781 | m_List.RemoveAt(aIndex); 782 | return tmp; 783 | } 784 | 785 | public override JSONNode Remove(JSONNode aNode) 786 | { 787 | m_List.Remove(aNode); 788 | return aNode; 789 | } 790 | 791 | public override void Clear() 792 | { 793 | m_List.Clear(); 794 | } 795 | 796 | public override JSONNode Clone() 797 | { 798 | var node = new JSONArray(); 799 | node.m_List.Capacity = m_List.Capacity; 800 | foreach(var n in m_List) 801 | { 802 | if (n != null) 803 | node.Add(n.Clone()); 804 | else 805 | node.Add(null); 806 | } 807 | return node; 808 | } 809 | 810 | public override IEnumerable Children 811 | { 812 | get 813 | { 814 | foreach (JSONNode N in m_List) 815 | yield return N; 816 | } 817 | } 818 | 819 | 820 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 821 | { 822 | aSB.Append('['); 823 | int count = m_List.Count; 824 | if (inline) 825 | aMode = JSONTextMode.Compact; 826 | for (int i = 0; i < count; i++) 827 | { 828 | if (i > 0) 829 | aSB.Append(','); 830 | if (aMode == JSONTextMode.Indent) 831 | aSB.AppendLine(); 832 | 833 | if (aMode == JSONTextMode.Indent) 834 | aSB.Append(' ', aIndent + aIndentInc); 835 | m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); 836 | } 837 | if (aMode == JSONTextMode.Indent) 838 | aSB.AppendLine().Append(' ', aIndent); 839 | aSB.Append(']'); 840 | } 841 | } 842 | // End of JSONArray 843 | 844 | public partial class JSONObject : JSONNode 845 | { 846 | private Dictionary m_Dict = new Dictionary(); 847 | 848 | private bool inline = false; 849 | public override bool Inline 850 | { 851 | get { return inline; } 852 | set { inline = value; } 853 | } 854 | 855 | public override JSONNodeType Tag { get { return JSONNodeType.Object; } } 856 | public override bool IsObject { get { return true; } } 857 | 858 | public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } 859 | 860 | 861 | public override JSONNode this[string aKey] 862 | { 863 | get 864 | { 865 | if (m_Dict.ContainsKey(aKey)) 866 | return m_Dict[aKey]; 867 | else 868 | return new JSONLazyCreator(this, aKey); 869 | } 870 | set 871 | { 872 | if (value == null) 873 | value = JSONNull.CreateOrGet(); 874 | if (m_Dict.ContainsKey(aKey)) 875 | m_Dict[aKey] = value; 876 | else 877 | m_Dict.Add(aKey, value); 878 | } 879 | } 880 | 881 | public override JSONNode this[int aIndex] 882 | { 883 | get 884 | { 885 | if (aIndex < 0 || aIndex >= m_Dict.Count) 886 | return null; 887 | return m_Dict.ElementAt(aIndex).Value; 888 | } 889 | set 890 | { 891 | if (value == null) 892 | value = JSONNull.CreateOrGet(); 893 | if (aIndex < 0 || aIndex >= m_Dict.Count) 894 | return; 895 | string key = m_Dict.ElementAt(aIndex).Key; 896 | m_Dict[key] = value; 897 | } 898 | } 899 | 900 | public override int Count 901 | { 902 | get { return m_Dict.Count; } 903 | } 904 | 905 | public override void Add(string aKey, JSONNode aItem) 906 | { 907 | if (aItem == null) 908 | aItem = JSONNull.CreateOrGet(); 909 | 910 | if (aKey != null) 911 | { 912 | if (m_Dict.ContainsKey(aKey)) 913 | m_Dict[aKey] = aItem; 914 | else 915 | m_Dict.Add(aKey, aItem); 916 | } 917 | else 918 | m_Dict.Add(Guid.NewGuid().ToString(), aItem); 919 | } 920 | 921 | public override JSONNode Remove(string aKey) 922 | { 923 | if (!m_Dict.ContainsKey(aKey)) 924 | return null; 925 | JSONNode tmp = m_Dict[aKey]; 926 | m_Dict.Remove(aKey); 927 | return tmp; 928 | } 929 | 930 | public override JSONNode Remove(int aIndex) 931 | { 932 | if (aIndex < 0 || aIndex >= m_Dict.Count) 933 | return null; 934 | var item = m_Dict.ElementAt(aIndex); 935 | m_Dict.Remove(item.Key); 936 | return item.Value; 937 | } 938 | 939 | public override JSONNode Remove(JSONNode aNode) 940 | { 941 | try 942 | { 943 | var item = m_Dict.Where(k => k.Value == aNode).First(); 944 | m_Dict.Remove(item.Key); 945 | return aNode; 946 | } 947 | catch 948 | { 949 | return null; 950 | } 951 | } 952 | 953 | public override void Clear() 954 | { 955 | m_Dict.Clear(); 956 | } 957 | 958 | public override JSONNode Clone() 959 | { 960 | var node = new JSONObject(); 961 | foreach (var n in m_Dict) 962 | { 963 | node.Add(n.Key, n.Value.Clone()); 964 | } 965 | return node; 966 | } 967 | 968 | public override bool HasKey(string aKey) 969 | { 970 | return m_Dict.ContainsKey(aKey); 971 | } 972 | 973 | public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) 974 | { 975 | JSONNode res; 976 | if (m_Dict.TryGetValue(aKey, out res)) 977 | return res; 978 | return aDefault; 979 | } 980 | 981 | public override IEnumerable Children 982 | { 983 | get 984 | { 985 | foreach (KeyValuePair N in m_Dict) 986 | yield return N.Value; 987 | } 988 | } 989 | 990 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 991 | { 992 | aSB.Append('{'); 993 | bool first = true; 994 | if (inline) 995 | aMode = JSONTextMode.Compact; 996 | foreach (var k in m_Dict) 997 | { 998 | if (!first) 999 | aSB.Append(','); 1000 | first = false; 1001 | if (aMode == JSONTextMode.Indent) 1002 | aSB.AppendLine(); 1003 | if (aMode == JSONTextMode.Indent) 1004 | aSB.Append(' ', aIndent + aIndentInc); 1005 | aSB.Append('\"').Append(Escape(k.Key)).Append('\"'); 1006 | if (aMode == JSONTextMode.Compact) 1007 | aSB.Append(':'); 1008 | else 1009 | aSB.Append(" : "); 1010 | k.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); 1011 | } 1012 | if (aMode == JSONTextMode.Indent) 1013 | aSB.AppendLine().Append(' ', aIndent); 1014 | aSB.Append('}'); 1015 | } 1016 | 1017 | } 1018 | // End of JSONObject 1019 | 1020 | public partial class JSONString : JSONNode 1021 | { 1022 | private string m_Data; 1023 | 1024 | public override JSONNodeType Tag { get { return JSONNodeType.String; } } 1025 | public override bool IsString { get { return true; } } 1026 | 1027 | public override Enumerator GetEnumerator() { return new Enumerator(); } 1028 | 1029 | 1030 | public override string Value 1031 | { 1032 | get { return m_Data; } 1033 | set 1034 | { 1035 | m_Data = value; 1036 | } 1037 | } 1038 | 1039 | public JSONString(string aData) 1040 | { 1041 | m_Data = aData; 1042 | } 1043 | public override JSONNode Clone() 1044 | { 1045 | return new JSONString(m_Data); 1046 | } 1047 | 1048 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 1049 | { 1050 | aSB.Append('\"').Append(Escape(m_Data)).Append('\"'); 1051 | } 1052 | public override bool Equals(object obj) 1053 | { 1054 | if (base.Equals(obj)) 1055 | return true; 1056 | string s = obj as string; 1057 | if (s != null) 1058 | return m_Data == s; 1059 | JSONString s2 = obj as JSONString; 1060 | if (s2 != null) 1061 | return m_Data == s2.m_Data; 1062 | return false; 1063 | } 1064 | public override int GetHashCode() 1065 | { 1066 | return m_Data.GetHashCode(); 1067 | } 1068 | public override void Clear() 1069 | { 1070 | m_Data = ""; 1071 | } 1072 | } 1073 | // End of JSONString 1074 | 1075 | public partial class JSONNumber : JSONNode 1076 | { 1077 | private double m_Data; 1078 | 1079 | public override JSONNodeType Tag { get { return JSONNodeType.Number; } } 1080 | public override bool IsNumber { get { return true; } } 1081 | public override Enumerator GetEnumerator() { return new Enumerator(); } 1082 | 1083 | public override string Value 1084 | { 1085 | get { return m_Data.ToString(CultureInfo.InvariantCulture); } 1086 | set 1087 | { 1088 | double v; 1089 | if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) 1090 | m_Data = v; 1091 | } 1092 | } 1093 | 1094 | public override double AsDouble 1095 | { 1096 | get { return m_Data; } 1097 | set { m_Data = value; } 1098 | } 1099 | public override long AsLong 1100 | { 1101 | get { return (long)m_Data; } 1102 | set { m_Data = value; } 1103 | } 1104 | public override ulong AsULong 1105 | { 1106 | get { return (ulong)m_Data; } 1107 | set { m_Data = value; } 1108 | } 1109 | 1110 | public JSONNumber(double aData) 1111 | { 1112 | m_Data = aData; 1113 | } 1114 | 1115 | public JSONNumber(string aData) 1116 | { 1117 | Value = aData; 1118 | } 1119 | 1120 | public override JSONNode Clone() 1121 | { 1122 | return new JSONNumber(m_Data); 1123 | } 1124 | 1125 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 1126 | { 1127 | aSB.Append(Value); 1128 | } 1129 | private static bool IsNumeric(object value) 1130 | { 1131 | return value is int || value is uint 1132 | || value is float || value is double 1133 | || value is decimal 1134 | || value is long || value is ulong 1135 | || value is short || value is ushort 1136 | || value is sbyte || value is byte; 1137 | } 1138 | public override bool Equals(object obj) 1139 | { 1140 | if (obj == null) 1141 | return false; 1142 | if (base.Equals(obj)) 1143 | return true; 1144 | JSONNumber s2 = obj as JSONNumber; 1145 | if (s2 != null) 1146 | return m_Data == s2.m_Data; 1147 | if (IsNumeric(obj)) 1148 | return Convert.ToDouble(obj) == m_Data; 1149 | return false; 1150 | } 1151 | public override int GetHashCode() 1152 | { 1153 | return m_Data.GetHashCode(); 1154 | } 1155 | public override void Clear() 1156 | { 1157 | m_Data = 0; 1158 | } 1159 | } 1160 | // End of JSONNumber 1161 | 1162 | public partial class JSONBool : JSONNode 1163 | { 1164 | private bool m_Data; 1165 | 1166 | public override JSONNodeType Tag { get { return JSONNodeType.Boolean; } } 1167 | public override bool IsBoolean { get { return true; } } 1168 | public override Enumerator GetEnumerator() { return new Enumerator(); } 1169 | 1170 | public override string Value 1171 | { 1172 | get { return m_Data.ToString(); } 1173 | set 1174 | { 1175 | bool v; 1176 | if (bool.TryParse(value, out v)) 1177 | m_Data = v; 1178 | } 1179 | } 1180 | public override bool AsBool 1181 | { 1182 | get { return m_Data; } 1183 | set { m_Data = value; } 1184 | } 1185 | 1186 | public JSONBool(bool aData) 1187 | { 1188 | m_Data = aData; 1189 | } 1190 | 1191 | public JSONBool(string aData) 1192 | { 1193 | Value = aData; 1194 | } 1195 | 1196 | public override JSONNode Clone() 1197 | { 1198 | return new JSONBool(m_Data); 1199 | } 1200 | 1201 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 1202 | { 1203 | aSB.Append((m_Data) ? "true" : "false"); 1204 | } 1205 | public override bool Equals(object obj) 1206 | { 1207 | if (obj == null) 1208 | return false; 1209 | if (obj is bool) 1210 | return m_Data == (bool)obj; 1211 | return false; 1212 | } 1213 | public override int GetHashCode() 1214 | { 1215 | return m_Data.GetHashCode(); 1216 | } 1217 | public override void Clear() 1218 | { 1219 | m_Data = false; 1220 | } 1221 | } 1222 | // End of JSONBool 1223 | 1224 | public partial class JSONNull : JSONNode 1225 | { 1226 | static JSONNull m_StaticInstance = new JSONNull(); 1227 | public static bool reuseSameInstance = true; 1228 | public static JSONNull CreateOrGet() 1229 | { 1230 | if (reuseSameInstance) 1231 | return m_StaticInstance; 1232 | return new JSONNull(); 1233 | } 1234 | private JSONNull() { } 1235 | 1236 | public override JSONNodeType Tag { get { return JSONNodeType.NullValue; } } 1237 | public override bool IsNull { get { return true; } } 1238 | public override Enumerator GetEnumerator() { return new Enumerator(); } 1239 | 1240 | public override string Value 1241 | { 1242 | get { return "null"; } 1243 | set { } 1244 | } 1245 | public override bool AsBool 1246 | { 1247 | get { return false; } 1248 | set { } 1249 | } 1250 | 1251 | public override JSONNode Clone() 1252 | { 1253 | return CreateOrGet(); 1254 | } 1255 | 1256 | public override bool Equals(object obj) 1257 | { 1258 | if (object.ReferenceEquals(this, obj)) 1259 | return true; 1260 | return (obj is JSONNull); 1261 | } 1262 | public override int GetHashCode() 1263 | { 1264 | return 0; 1265 | } 1266 | 1267 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 1268 | { 1269 | aSB.Append("null"); 1270 | } 1271 | } 1272 | // End of JSONNull 1273 | 1274 | internal partial class JSONLazyCreator : JSONNode 1275 | { 1276 | private JSONNode m_Node = null; 1277 | private string m_Key = null; 1278 | public override JSONNodeType Tag { get { return JSONNodeType.None; } } 1279 | public override Enumerator GetEnumerator() { return new Enumerator(); } 1280 | 1281 | public JSONLazyCreator(JSONNode aNode) 1282 | { 1283 | m_Node = aNode; 1284 | m_Key = null; 1285 | } 1286 | 1287 | public JSONLazyCreator(JSONNode aNode, string aKey) 1288 | { 1289 | m_Node = aNode; 1290 | m_Key = aKey; 1291 | } 1292 | 1293 | private T Set(T aVal) where T : JSONNode 1294 | { 1295 | if (m_Key == null) 1296 | m_Node.Add(aVal); 1297 | else 1298 | m_Node.Add(m_Key, aVal); 1299 | m_Node = null; // Be GC friendly. 1300 | return aVal; 1301 | } 1302 | 1303 | public override JSONNode this[int aIndex] 1304 | { 1305 | get { return new JSONLazyCreator(this); } 1306 | set { Set(new JSONArray()).Add(value); } 1307 | } 1308 | 1309 | public override JSONNode this[string aKey] 1310 | { 1311 | get { return new JSONLazyCreator(this, aKey); } 1312 | set { Set(new JSONObject()).Add(aKey, value); } 1313 | } 1314 | 1315 | public override void Add(JSONNode aItem) 1316 | { 1317 | Set(new JSONArray()).Add(aItem); 1318 | } 1319 | 1320 | public override void Add(string aKey, JSONNode aItem) 1321 | { 1322 | Set(new JSONObject()).Add(aKey, aItem); 1323 | } 1324 | 1325 | public static bool operator ==(JSONLazyCreator a, object b) 1326 | { 1327 | if (b == null) 1328 | return true; 1329 | return System.Object.ReferenceEquals(a, b); 1330 | } 1331 | 1332 | public static bool operator !=(JSONLazyCreator a, object b) 1333 | { 1334 | return !(a == b); 1335 | } 1336 | 1337 | public override bool Equals(object obj) 1338 | { 1339 | if (obj == null) 1340 | return true; 1341 | return System.Object.ReferenceEquals(this, obj); 1342 | } 1343 | 1344 | public override int GetHashCode() 1345 | { 1346 | return 0; 1347 | } 1348 | 1349 | public override int AsInt 1350 | { 1351 | get { Set(new JSONNumber(0)); return 0; } 1352 | set { Set(new JSONNumber(value)); } 1353 | } 1354 | 1355 | public override float AsFloat 1356 | { 1357 | get { Set(new JSONNumber(0.0f)); return 0.0f; } 1358 | set { Set(new JSONNumber(value)); } 1359 | } 1360 | 1361 | public override double AsDouble 1362 | { 1363 | get { Set(new JSONNumber(0.0)); return 0.0; } 1364 | set { Set(new JSONNumber(value)); } 1365 | } 1366 | 1367 | public override long AsLong 1368 | { 1369 | get 1370 | { 1371 | if (longAsString) 1372 | Set(new JSONString("0")); 1373 | else 1374 | Set(new JSONNumber(0.0)); 1375 | return 0L; 1376 | } 1377 | set 1378 | { 1379 | if (longAsString) 1380 | Set(new JSONString(value.ToString())); 1381 | else 1382 | Set(new JSONNumber(value)); 1383 | } 1384 | } 1385 | 1386 | public override ulong AsULong 1387 | { 1388 | get 1389 | { 1390 | if (longAsString) 1391 | Set(new JSONString("0")); 1392 | else 1393 | Set(new JSONNumber(0.0)); 1394 | return 0L; 1395 | } 1396 | set 1397 | { 1398 | if (longAsString) 1399 | Set(new JSONString(value.ToString())); 1400 | else 1401 | Set(new JSONNumber(value)); 1402 | } 1403 | } 1404 | 1405 | public override bool AsBool 1406 | { 1407 | get { Set(new JSONBool(false)); return false; } 1408 | set { Set(new JSONBool(value)); } 1409 | } 1410 | 1411 | public override JSONArray AsArray 1412 | { 1413 | get { return Set(new JSONArray()); } 1414 | } 1415 | 1416 | public override JSONObject AsObject 1417 | { 1418 | get { return Set(new JSONObject()); } 1419 | } 1420 | internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) 1421 | { 1422 | aSB.Append("null"); 1423 | } 1424 | } 1425 | // End of JSONLazyCreator 1426 | 1427 | public static class JSON 1428 | { 1429 | public static JSONNode Parse(string aJSON) 1430 | { 1431 | return JSONNode.Parse(aJSON); 1432 | } 1433 | } 1434 | } -------------------------------------------------------------------------------- /Editor/SimpleJSON.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9023abb2c57ee2478769f1b1b6c9a99 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 BennyKok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fa67a257ff9c49488afe2b0582a27bd 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notion API for Unity 2 | 3 | This is very initial draft of non-official Notion Beta API Client for Unity 4 | 5 | ## Aim 6 | 7 | - Zero dependencies 8 | - Optional Json parser 9 | - Focus on Database related endpoint 10 | - Using Unity WebRequest + JsonUtility, basically should work on all platform 11 | 12 | ## Example 13 | 14 | Creating a NotionAPI object. 15 | 16 | ```csharp 17 | var api = new NotionAPI(apiKey); 18 | ``` 19 | 20 | Example db scenario 21 | 22 | ### Card Database 23 | 24 | | Name (Title) | Tags (Multi-select) | 25 | | ------------- | :-----------------: | 26 | | Basic Attack | Attack, Defense | 27 | | Ranged Attack | Defense | 28 | 29 | Getting the database object and querying the database as JSON. 30 | 31 | ```csharp 32 | private IEnumerator Start() 33 | { 34 | var api = new NotionAPI(apiKey); 35 | 36 | yield return api.GetDatabase(database_id, (db) => 37 | { 38 | Debug.Log(db.id); 39 | Debug.Log(db.created_time); 40 | Debug.Log(db.title.First().text.content); 41 | }); 42 | 43 | yield return api.QueryDatabaseJSON(database_id, (db) => 44 | { 45 | Debug.Log(db); 46 | }); 47 | } 48 | 49 | // For type parsing the db Property with JsonUtility 50 | [Serializable] 51 | public class CardDatabaseProperties 52 | { 53 | public MultiSelectProperty Tags; 54 | public TitleProperty Name; 55 | } 56 | ``` 57 | 58 | ## TO-DO 59 | 60 | - [x] Implement basic data model for Page 61 | - [x] Type parsed method for QueryDatabaseJSON 62 | - [ ] Data Model accessibility (Implicit operator + Date conversion) 63 | - [ ] Filter options for db query? -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7cec54dd7a7884e459227a382e00183d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b717b10fb4bb6ab4e8e247596040056e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/BennyKok.NotionAPI.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BennyKok.NotionAPI", 3 | "rootNamespace": "BennyKok.NotionAPI", 4 | "references": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [], 13 | "noEngineReferences": false 14 | } -------------------------------------------------------------------------------- /Runtime/BennyKok.NotionAPI.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9a678814a577206439b7c76fbbddfba3 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/DataModel.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c3a95a42f50cac48bd90d85b54e9e3c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/DataModel/Block.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace BennyKok.NotionAPI 5 | { 6 | [Serializable] 7 | public class Text 8 | { 9 | public string type; 10 | public TextContent text; 11 | public Annotations annotations; 12 | public string plain_text; 13 | public string href; 14 | 15 | 16 | [Serializable] 17 | public class TextContent 18 | { 19 | public string content; 20 | public string link; 21 | 22 | public static implicit operator string(TextContent text) 23 | { 24 | if (text == null) return null; 25 | return !string.IsNullOrEmpty(text.content) ? text.content : text.link; 26 | } 27 | } 28 | 29 | [Serializable] 30 | public class Annotations 31 | { 32 | public bool bold; 33 | public bool italic; 34 | public bool strikethrough; 35 | public bool underline; 36 | public bool code; 37 | public string color; 38 | } 39 | } 40 | 41 | [Serializable] 42 | public class Date 43 | { 44 | public string start; 45 | public string end; 46 | } 47 | } -------------------------------------------------------------------------------- /Runtime/DataModel/Block.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c9aff65907659f1488ec30c9c701cb8b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/DataModel/Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BennyKok.NotionAPI 4 | { 5 | [Serializable] 6 | public class IDObject 7 | { 8 | public string id; 9 | public string created_time; 10 | public string last_edited_time; 11 | } 12 | 13 | [Serializable] 14 | public class Database : IDObject 15 | { 16 | public Text[] title; 17 | public T properties; 18 | } 19 | 20 | [Serializable] 21 | public class Page : IDObject 22 | { 23 | public T properties; 24 | } 25 | 26 | [Serializable] 27 | public class DatabaseQueryResponse 28 | { 29 | public Page[] results; 30 | } 31 | 32 | [Serializable] 33 | public class DatabaseUsers 34 | { 35 | public UserObject[] results; 36 | } 37 | } -------------------------------------------------------------------------------- /Runtime/DataModel/Common.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e581684c8dd22094da8374c5b295d3ad 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/DataModel/Property.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BennyKok.NotionAPI 4 | { 5 | [Serializable] 6 | public class Property 7 | { 8 | public string type; 9 | public string id; 10 | } 11 | 12 | [Serializable] 13 | public class OptionEntry 14 | { 15 | public string id; 16 | public string name; 17 | public string color; 18 | } 19 | 20 | [Serializable] 21 | public class Options 22 | { 23 | public OptionEntry[] options; 24 | } 25 | 26 | [Serializable] 27 | public class MultiSelectPropertyDefinition : Property 28 | { 29 | public Options multi_select; 30 | } 31 | 32 | [Serializable] 33 | public class SelectProperty : Property 34 | { 35 | public OptionEntry select; 36 | public OptionEntry Value => select; 37 | } 38 | 39 | [Serializable] 40 | public class MultiSelectProperty : Property 41 | { 42 | public OptionEntry[] multi_select; 43 | public OptionEntry[] Value => multi_select; 44 | } 45 | 46 | [Serializable] 47 | public class SelectPropertyDefinition : Property 48 | { 49 | public Options select; 50 | } 51 | 52 | [Serializable] 53 | public class TitleProperty : Property 54 | { 55 | public Text[] title; 56 | public string Value 57 | => (title != null && title.Length > 0) ? title[0].text : null; 58 | } 59 | 60 | [Serializable] 61 | public class TextPropertyDefinition : Property 62 | { 63 | public Text[] text; 64 | public string Value 65 | => (text != null && text.Length > 0) ? text[0].plain_text : null; 66 | } 67 | 68 | [Serializable] 69 | public class TextProperty : Property 70 | { 71 | public Text[] rich_text; 72 | public string Value 73 | => (rich_text != null && rich_text.Length > 0) ? rich_text[0].plain_text : null; 74 | } 75 | 76 | [Serializable] 77 | public class FormulaStringProperty : Property 78 | { 79 | public FormulaString formula; 80 | public string Value 81 | => formula.@string; 82 | 83 | [Serializable] 84 | public class FormulaString 85 | { 86 | public string type; 87 | public string @string; 88 | } 89 | } 90 | 91 | [Serializable] 92 | public class NumberProperty : Property 93 | { 94 | public float number; 95 | public float Value => number; 96 | 97 | public override string ToString() => number.ToString(); 98 | } 99 | 100 | [Serializable] 101 | public class CheckboxProperty : Property 102 | { 103 | public bool checkbox; 104 | public bool Value => checkbox; 105 | 106 | public override string ToString() => checkbox.ToString(); 107 | } 108 | 109 | [Serializable] 110 | public class DateProperty : Property 111 | { 112 | public Date date; 113 | } 114 | 115 | [Serializable] 116 | public class Person 117 | { 118 | public string email; 119 | } 120 | 121 | [Serializable] 122 | public class UserObject : Property 123 | { 124 | public string name; 125 | public string avatar_url; 126 | public Person person; 127 | } 128 | 129 | [Serializable] 130 | public class PeopleProperty : Property 131 | { 132 | public UserObject[] people; 133 | public UserObject[] Value => people; 134 | } 135 | } -------------------------------------------------------------------------------- /Runtime/DataModel/Property.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1531557ea37c13499b40dabbcdc4d19 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/DatabaseSchema.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | using UnityEngine; 5 | 6 | namespace BennyKok.NotionAPI 7 | { 8 | [CreateAssetMenu(fileName = "DatabaseSchema", menuName = "Notion API/DatabaseSchema", order = 0)] 9 | public class DatabaseSchema : ScriptableObject 10 | { 11 | public string apiKey; 12 | public string database_id; 13 | public List fieldNames; 14 | public List fieldTypes; 15 | } 16 | } -------------------------------------------------------------------------------- /Runtime/DatabaseSchema.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 31f2f54e6cb1cf04ca788fe8a3ad9a5d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/EditorButtonAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace BennyKok.NotionAPI 6 | { 7 | [System.AttributeUsage(System.AttributeTargets.Field)] 8 | public class EditorButtonAttribute : PropertyAttribute 9 | { 10 | public readonly string methodName; 11 | public readonly string label; 12 | 13 | public EditorButtonAttribute(string label, string methodName) 14 | { 15 | this.methodName = methodName; 16 | this.label = label; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Runtime/EditorButtonAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d70122b4337db644a8cd97f141523740 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/NotionAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using UnityEngine; 4 | using UnityEngine.Networking; 5 | 6 | namespace BennyKok.NotionAPI 7 | { 8 | public class NotionAPI 9 | { 10 | private bool debug = true; 11 | private string apiKey; 12 | private readonly static string version = "v1"; 13 | private readonly static string rootUrl = $"https://api.notion.com/{version}"; 14 | private readonly static string urlDB = rootUrl + "/databases"; 15 | private readonly static string urlUsers = rootUrl + "/users"; 16 | 17 | public NotionAPI(string apiKey) 18 | { 19 | this.apiKey = apiKey; 20 | } 21 | 22 | enum RequestType 23 | { 24 | GET, POST 25 | } 26 | 27 | private UnityWebRequest WebRequestWithAuth(string url, RequestType requestType, WWWForm form = null) 28 | { 29 | UnityWebRequest request = null; 30 | switch (requestType) 31 | { 32 | case RequestType.GET: 33 | request = UnityWebRequest.Get(url); 34 | break; 35 | case RequestType.POST: 36 | request = UnityWebRequest.Post(url, form); 37 | break; 38 | } 39 | request.SetRequestHeader("Authorization", $"Bearer {apiKey}"); 40 | request.SetRequestHeader("Content-Type", "application/json"); 41 | return request; 42 | } 43 | 44 | public IEnumerator GetJSON(string url, Action callback) 45 | { 46 | if (debug) Debug.Log("GET Requesting: " + url); 47 | using (var request = WebRequestWithAuth(url, RequestType.GET)) 48 | { 49 | yield return request.SendWebRequest(); 50 | var data = request.downloadHandler.text; 51 | callback(data); 52 | } 53 | } 54 | 55 | public IEnumerator PostJSON(string url, Action callback, WWWForm form) 56 | { 57 | if (debug) Debug.Log("POST Requesting: " + url); 58 | using (var request = WebRequestWithAuth(url, RequestType.POST, form)) 59 | { 60 | yield return request.SendWebRequest(); 61 | var data = request.downloadHandler.text; 62 | callback(data); 63 | } 64 | } 65 | 66 | /// 67 | /// Get the Notion Database JSON object parsed with Unity's JsonUtility 68 | /// 69 | /// Database Id 70 | /// 71 | /// An serializable class containing all Property field for the Json parsing 72 | /// 73 | public IEnumerator GetDatabase(string database_id, Action> callback) 74 | { 75 | yield return GetDatabaseJSON(database_id, (json) => 76 | { 77 | if (debug) Debug.Log(json); 78 | callback(JsonUtility.FromJson>(json)); 79 | }); 80 | } 81 | 82 | /// 83 | /// Return the entire Notion Database schema in raw JSON string 84 | /// 85 | /// Database Id 86 | /// 87 | /// 88 | public IEnumerator GetDatabaseJSON(string database_id, Action callback) 89 | { 90 | var url = $"{urlDB}/{database_id}"; 91 | yield return GetJSON(url, callback); 92 | } 93 | 94 | public IEnumerator QueryDatabase(string database_id, Action> callback) 95 | { 96 | yield return QueryDatabaseJSON(database_id, (json) => 97 | { 98 | if (debug) Debug.Log(json); 99 | callback(JsonUtility.FromJson>(json)); 100 | }); 101 | } 102 | 103 | public IEnumerator QueryDatabaseJSON(string database_id, Action callback) 104 | { 105 | var url = $"{urlDB}/{database_id}/query"; 106 | yield return PostJSON(url, callback, null); 107 | } 108 | 109 | public IEnumerator GetUsers(Action callback) 110 | { 111 | var url = $"{urlUsers}/"; 112 | 113 | yield return GetJSON(url, (json) => 114 | { 115 | if (debug) Debug.Log(json); 116 | callback(JsonUtility.FromJson(json)); 117 | }); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Runtime/NotionAPI.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa643bb1def297e4abab6bc524173b63 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/NotionAPITest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace BennyKok.NotionAPI 7 | { 8 | public class NotionAPITest : MonoBehaviour 9 | { 10 | public string apiKey; 11 | public string database_id; 12 | 13 | private IEnumerator Start() 14 | { 15 | var api = new NotionAPI(apiKey); 16 | 17 | yield return api.GetDatabase(database_id, (db) => 18 | { 19 | Debug.Log(db.id); 20 | Debug.Log(db.created_time); 21 | Debug.Log(db.title.First().text.content); 22 | 23 | Debug.Log(JsonUtility.ToJson(db)); 24 | }); 25 | 26 | yield return api.QueryDatabase(database_id, (db) => 27 | { 28 | Debug.Log(JsonUtility.ToJson(db)); 29 | }); 30 | } 31 | 32 | [Serializable] 33 | public class CardDatabasePropertiesDefinition 34 | { 35 | public MultiSelectPropertyDefinition Tags; 36 | public TitleProperty Name; 37 | public CheckboxProperty IsActive; 38 | public DateProperty Date; 39 | public SelectPropertyDefinition Type; 40 | public NumberProperty number; 41 | public TextProperty Description; 42 | } 43 | 44 | [Serializable] 45 | public class CardDatabaseProperties 46 | { 47 | public MultiSelectProperty Tags; 48 | public TitleProperty Name; 49 | public CheckboxProperty IsActive; 50 | public DateProperty Date; 51 | public SelectProperty Type; 52 | public NumberProperty number; 53 | public NumberProperty UseTime; 54 | public TextProperty Description; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Runtime/NotionAPITest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b8976381bfeca449957073697d2f8ed 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notion-api", 3 | "version": "1.0.0", 4 | "description": "A simple API wrapper for Notion API", 5 | "author": "BennyKok", 6 | "displayName": "Notion API", 7 | "dependencies": { 8 | "com.unity.editorcoroutines": "1.0.0" 9 | } 10 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9c939ba42ac74b0479cd5e8b9092ee27 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------