v1.2
This commit is contained in:
252
FSI.BT.Tools/SystemTrayMenu/Helpers/Updater/GitHubUpdate.cs
Normal file
252
FSI.BT.Tools/SystemTrayMenu/Helpers/Updater/GitHubUpdate.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
//// <copyright file="GitHubUpdate.cs" company="PlaceholderCompany">
|
||||
//// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
//// </copyright>
|
||||
|
||||
//namespace FSI.BT.Tools.SystemTrayMenu.Helper.Updater
|
||||
//{
|
||||
// using System;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Drawing;
|
||||
// using System.Net.Http;
|
||||
// using System.Reflection;
|
||||
// using System.Windows.Forms;
|
||||
// using FSI.BT.Tools.Global.Utilities;
|
||||
// using FSI.BT.Tools.SystemTrayMenu.Utilities;
|
||||
|
||||
// public class GitHubUpdate
|
||||
// {
|
||||
// private static List<Dictionary<string, object>> releases;
|
||||
// private static Form newVersionForm;
|
||||
|
||||
// public static void ActivateNewVersionFormOrCheckForUpdates(bool showWhenUpToDate)
|
||||
// {
|
||||
// if (newVersionForm != null)
|
||||
// {
|
||||
// newVersionForm.HandleInvoke(newVersionForm.Activate);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// CheckForUpdates(showWhenUpToDate);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private static void CheckForUpdates(bool showWhenUpToDate)
|
||||
// {
|
||||
// string urlGithubReleases = @"http://api.github.com/repos/Hofknecht/SystemTrayMenu/releases";
|
||||
// HttpClient client = new();
|
||||
|
||||
// // https://developer.github.com/v3/#user-agent-required
|
||||
// client.DefaultRequestHeaders.Add("User-Agent", "FSI.BT.Tools.SystemTrayMenu/" + Application.ProductVersion.ToString());
|
||||
|
||||
// // https://developer.github.com/v3/media/#request-specific-version
|
||||
// client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3.text+json");
|
||||
|
||||
// try
|
||||
// {
|
||||
// using HttpResponseMessage response = client.GetAsync(urlGithubReleases).Result;
|
||||
// using HttpContent content = response.Content;
|
||||
// string responseString = content.ReadAsStringAsync().Result;
|
||||
// releases = responseString.FromJson<List<Dictionary<string, object>>>();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Warn($"{nameof(CheckForUpdates)} failed", ex);
|
||||
// }
|
||||
|
||||
// if (releases == null)
|
||||
// {
|
||||
// Log.Info($"{nameof(CheckForUpdates)} failed.");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// RemoveCurrentAndOlderVersions();
|
||||
// ShowNewVersionOrUpToDateDialog(showWhenUpToDate);
|
||||
// }
|
||||
|
||||
// newVersionForm?.Dispose();
|
||||
// newVersionForm = null;
|
||||
// }
|
||||
|
||||
// private static void RemoveCurrentAndOlderVersions()
|
||||
// {
|
||||
// int releasesCount = releases.Count;
|
||||
// Version versionCurrent = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
// for (int i = 0; i < releasesCount; i++)
|
||||
// {
|
||||
// string tagName = releases[i]["tag_name"].ToString();
|
||||
// Version versionGitHub = new(tagName.Replace("v", string.Empty));
|
||||
// if (versionGitHub.CompareTo(versionCurrent) < 1)
|
||||
// {
|
||||
// releases.RemoveRange(i, releasesCount - i);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private static void ShowNewVersionOrUpToDateDialog(bool showWhenUpToDate)
|
||||
// {
|
||||
// if (releases.Count > 0)
|
||||
// {
|
||||
// if (NewVersionDialog() == DialogResult.Yes)
|
||||
// {
|
||||
// Log.ProcessStart("https://github.com/Hofknecht/SystemTrayMenu/releases");
|
||||
// }
|
||||
// }
|
||||
// else if (showWhenUpToDate)
|
||||
// {
|
||||
// MessageBox.Show(Global.Utilities.Translator.GetText("You have the latest version of SystemTrayMenu!"));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Creates a window to show changelog of new available versions.
|
||||
// /// </summary>
|
||||
// /// <param name="LatestVersionTitle">Name of latest release.</param>
|
||||
// /// <param name="Changelog">Pathnotes.</param>
|
||||
// /// <returns>OK = OK, Yes = Website, else = Cancel.</returns>
|
||||
// private static DialogResult NewVersionDialog()
|
||||
// {
|
||||
// const int ClientPad = 15;
|
||||
// newVersionForm = new()
|
||||
// {
|
||||
// StartPosition = FormStartPosition.CenterScreen,
|
||||
// FormBorderStyle = FormBorderStyle.FixedDialog,
|
||||
// Icon = Config.GetAppIcon(),
|
||||
// ShowInTaskbar = false,
|
||||
// };
|
||||
// newVersionForm.FormBorderStyle = FormBorderStyle.Sizable;
|
||||
// newVersionForm.MaximizeBox = true;
|
||||
// newVersionForm.MinimizeBox = false;
|
||||
// newVersionForm.ClientSize = new Size(600, 400);
|
||||
// newVersionForm.MinimumSize = newVersionForm.ClientSize;
|
||||
// newVersionForm.Text = Global.Utilities.Translator.GetText("New version available!");
|
||||
|
||||
// Label label = new()
|
||||
// {
|
||||
// Size = new Size(newVersionForm.ClientSize.Width - ClientPad, 20),
|
||||
// Location = new Point(ClientPad, ClientPad),
|
||||
// Text = $"{Global.Utilities.Translator.GetText("Latest available version:")} {GetLatestVersionName()}",
|
||||
// };
|
||||
// newVersionForm.Controls.Add(label);
|
||||
|
||||
// Button buttonOK = new()
|
||||
// {
|
||||
// DialogResult = DialogResult.OK,
|
||||
// Name = "buttonOK",
|
||||
// };
|
||||
// buttonOK.Location = new Point(
|
||||
// newVersionForm.ClientSize.Width - buttonOK.Size.Width - ClientPad,
|
||||
// newVersionForm.ClientSize.Height - buttonOK.Size.Height - ClientPad);
|
||||
// buttonOK.MinimumSize = new Size(75, 23);
|
||||
// buttonOK.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
// buttonOK.Text = Global.Utilities.Translator.GetText("OK");
|
||||
// buttonOK.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
// buttonOK.AutoSize = true;
|
||||
// newVersionForm.Controls.Add(buttonOK);
|
||||
|
||||
// Button buttonGoToDownloadPage = new()
|
||||
// {
|
||||
// DialogResult = DialogResult.Yes,
|
||||
// Name = "buttonGoToDownloadPage",
|
||||
// };
|
||||
// buttonGoToDownloadPage.Location = new Point(
|
||||
// newVersionForm.ClientSize.Width - buttonGoToDownloadPage.Size.Width - ClientPad - buttonOK.Size.Width - ClientPad,
|
||||
// newVersionForm.ClientSize.Height - buttonGoToDownloadPage.Size.Height - ClientPad);
|
||||
// buttonGoToDownloadPage.MinimumSize = new Size(75, 23);
|
||||
// buttonGoToDownloadPage.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
// buttonGoToDownloadPage.Text = Global.Utilities.Translator.GetText("Go to download page");
|
||||
// buttonGoToDownloadPage.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||
// buttonGoToDownloadPage.AutoSize = true;
|
||||
// newVersionForm.Controls.Add(buttonGoToDownloadPage);
|
||||
|
||||
// TextBox textBox = new()
|
||||
// {
|
||||
// Location = new Point(ClientPad, label.Location.Y + label.Size.Height + 5),
|
||||
// };
|
||||
// textBox.Size = new Size(
|
||||
// newVersionForm.ClientSize.Width - (ClientPad * 2),
|
||||
// buttonOK.Location.Y - ClientPad - textBox.Location.Y);
|
||||
// textBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
// textBox.Multiline = true;
|
||||
// textBox.Text = GetChangelog();
|
||||
// textBox.ReadOnly = true;
|
||||
// textBox.ScrollBars = ScrollBars.Both;
|
||||
// textBox.BackColor = Color.FromKnownColor(KnownColor.Window);
|
||||
// textBox.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
|
||||
// newVersionForm.Controls.Add(textBox);
|
||||
|
||||
// newVersionForm.AcceptButton = buttonOK;
|
||||
// return newVersionForm.ShowDialog();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Returns the latest release version name.
|
||||
// /// </summary>
|
||||
// /// <returns>Version name.</returns>
|
||||
// private static string GetLatestVersionName()
|
||||
// {
|
||||
// string result = "Unknown";
|
||||
|
||||
// if (releases == null)
|
||||
// {
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// result = releases[0]["tag_name"].ToString().Replace("v", string.Empty);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Warn($"{nameof(GetLatestVersionName)} failed", ex);
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Returns the change log from current version up to the latest release version.
|
||||
// /// </summary>
|
||||
// /// <returns>Change log summary or error text.</returns>
|
||||
// private static string GetChangelog()
|
||||
// {
|
||||
// string result = string.Empty;
|
||||
// string errorstr = "An error occurred during update check!" + Environment.NewLine;
|
||||
|
||||
// if (releases == null)
|
||||
// {
|
||||
// return errorstr + "Could not receive changelog!";
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// for (int i = 0; i < releases.Count; i++)
|
||||
// {
|
||||
// Dictionary<string, object> release = releases[i];
|
||||
|
||||
// result += release["name"].ToString()
|
||||
// + Environment.NewLine
|
||||
// + release["body_text"].ToString()
|
||||
// .Replace("\n\n", Environment.NewLine)
|
||||
// .Replace("\n \n", Environment.NewLine)
|
||||
// + Environment.NewLine + Environment.NewLine;
|
||||
// if (i < releases.Count)
|
||||
// {
|
||||
// result += "--------------------------------------------------" +
|
||||
// "-------------------------------------------------------"
|
||||
// + Environment.NewLine;
|
||||
// }
|
||||
// }
|
||||
|
||||
// result = result.Replace("\n", Environment.NewLine);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Warn($"{nameof(GetChangelog)}", ex);
|
||||
// result = errorstr + ex.Message.ToString();
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
480
FSI.BT.Tools/SystemTrayMenu/Helpers/Updater/JsonParser.cs
Normal file
480
FSI.BT.Tools/SystemTrayMenu/Helpers/Updater/JsonParser.cs
Normal file
@@ -0,0 +1,480 @@
|
||||
// <copyright file="JsonParser.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace FSI.BT.Tools.SystemTrayMenu.Helper.Updater
|
||||
{
|
||||
// Copyright (c) 2018 Alex Parker
|
||||
// Copyright (c) 2018-2019 Peter Kirmeier
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
// Really simple JSON parser in ~300 lines
|
||||
// - Attempts to parse JSON files with minimal GC allocation
|
||||
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
|
||||
// - Classes and structs can be parsed too!
|
||||
// class Foo { public int Value; }
|
||||
// "{\"Value\":10}".FromJson<Foo>()
|
||||
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
|
||||
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
|
||||
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
|
||||
// - No JIT Emit support to support AOT compilation on iOS
|
||||
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
|
||||
// - Only public fields and property setters on classes/structs will be written to
|
||||
//
|
||||
// Limitations:
|
||||
// - No JIT Emit support to parse structures quickly
|
||||
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
|
||||
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
|
||||
public static class JSONParser
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static Stack<List<string>> splitArrayPool;
|
||||
[ThreadStatic]
|
||||
private static StringBuilder stringBuilder;
|
||||
[ThreadStatic]
|
||||
private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache;
|
||||
[ThreadStatic]
|
||||
private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache;
|
||||
|
||||
public static T FromJson<T>(this string json)
|
||||
{
|
||||
// Initialize, if needed, the ThreadStatic variables
|
||||
propertyInfoCache ??= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
|
||||
fieldInfoCache ??= new Dictionary<Type, Dictionary<string, FieldInfo>>();
|
||||
|
||||
stringBuilder ??= new StringBuilder();
|
||||
|
||||
splitArrayPool ??= new Stack<List<string>>();
|
||||
|
||||
// Remove all whitespace not within strings to make parsing simpler
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 0; i < json.Length; i++)
|
||||
{
|
||||
char c = json[i];
|
||||
if (c == '"')
|
||||
{
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char.IsWhiteSpace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
// Parse the thing!
|
||||
return (T)ParseValue(typeof(T), stringBuilder.ToString());
|
||||
}
|
||||
|
||||
internal static object ParseValue(Type type, string json)
|
||||
{
|
||||
if (type == typeof(string))
|
||||
{
|
||||
if (json.Length <= 2)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
StringBuilder parseStringBuilder = new(json.Length);
|
||||
for (int i = 1; i < json.Length - 1; ++i)
|
||||
{
|
||||
if (json[i] == '\\' && i + 1 < json.Length - 1)
|
||||
{
|
||||
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
|
||||
if (j >= 0)
|
||||
{
|
||||
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
|
||||
{
|
||||
if (uint.TryParse(json.AsSpan(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out uint c))
|
||||
{
|
||||
parseStringBuilder.Append((char)c);
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseStringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
return parseStringBuilder.ToString();
|
||||
}
|
||||
|
||||
if (type.IsPrimitive)
|
||||
{
|
||||
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out decimal result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (json == "null")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
if (json[0] == '"')
|
||||
{
|
||||
json = json[1..^1];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, json, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
Type arrayType = type.GetElementType();
|
||||
if (json[0] != '[' || json[^1] != ']')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<string> elems = Split(json);
|
||||
Array newArray = Array.CreateInstance(arrayType, elems.Count);
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
{
|
||||
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
|
||||
}
|
||||
|
||||
splitArrayPool.Push(elems);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
Type listType = type.GetGenericArguments()[0];
|
||||
if (json[0] != '[' || json[^1] != ']')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<string> elems = Split(json);
|
||||
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
|
||||
for (int i = 0; i < elems.Count; i++)
|
||||
{
|
||||
list.Add(ParseValue(listType, elems[i]));
|
||||
}
|
||||
|
||||
splitArrayPool.Push(elems);
|
||||
return list;
|
||||
}
|
||||
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
Type keyType, valueType;
|
||||
{
|
||||
Type[] args = type.GetGenericArguments();
|
||||
keyType = args[0];
|
||||
valueType = args[1];
|
||||
}
|
||||
|
||||
// Refuse to parse dictionary keys that aren't of type string
|
||||
if (keyType != typeof(string))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Must be a valid dictionary element
|
||||
if (json[0] != '{' || json[^1] != '}')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string keyValue = elems[i][1..^1];
|
||||
object val = ParseValue(valueType, elems[i + 1]);
|
||||
dictionary.Add(keyValue, val);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
if (type == typeof(object))
|
||||
{
|
||||
return ParseAnonymousValue(json);
|
||||
}
|
||||
|
||||
if (json[0] == '{' && json[^1] == '}')
|
||||
{
|
||||
return ParseObject(type, json);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
|
||||
{
|
||||
stringBuilder.Append(json[startIdx]);
|
||||
for (int i = startIdx + 1; i < json.Length; i++)
|
||||
{
|
||||
if (json[i] == '\\')
|
||||
{
|
||||
if (appendEscapeCharacter)
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
stringBuilder.Append(json[i + 1]);
|
||||
i++; // Skip next character as it is escaped
|
||||
}
|
||||
else if (json[i] == '"')
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return json.Length - 1;
|
||||
}
|
||||
|
||||
// Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
|
||||
private static List<string> Split(string json)
|
||||
{
|
||||
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>();
|
||||
splitArray.Clear();
|
||||
if (json.Length == 2)
|
||||
{
|
||||
return splitArray;
|
||||
}
|
||||
|
||||
int parseDepth = 0;
|
||||
stringBuilder.Length = 0;
|
||||
for (int i = 1; i < json.Length - 1; i++)
|
||||
{
|
||||
switch (json[i])
|
||||
{
|
||||
case '[':
|
||||
case '{':
|
||||
parseDepth++;
|
||||
break;
|
||||
case ']':
|
||||
case '}':
|
||||
parseDepth--;
|
||||
break;
|
||||
case '"':
|
||||
i = AppendUntilStringEnd(true, i, json);
|
||||
continue;
|
||||
case ',':
|
||||
case ':':
|
||||
if (parseDepth == 0)
|
||||
{
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
stringBuilder.Length = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
stringBuilder.Append(json[i]);
|
||||
}
|
||||
|
||||
splitArray.Add(stringBuilder.ToString());
|
||||
|
||||
return splitArray;
|
||||
}
|
||||
|
||||
private static object ParseAnonymousValue(string json)
|
||||
{
|
||||
if (json.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (json[0] == '{' && json[^1] == '}')
|
||||
{
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<string, object>(elems.Count / 2);
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
dict.Add(elems[i][1..^1], ParseAnonymousValue(elems[i + 1]));
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
if (json[0] == '[' && json[^1] == ']')
|
||||
{
|
||||
List<string> items = Split(json);
|
||||
var finalList = new List<object>(items.Count);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
finalList.Add(ParseAnonymousValue(items[i]));
|
||||
}
|
||||
|
||||
return finalList;
|
||||
}
|
||||
|
||||
if (json[0] == '"' && json[^1] == '"')
|
||||
{
|
||||
return ParseValue(typeof(string), json); // fix https://github.com/zanders3/json/issues/29
|
||||
}
|
||||
|
||||
if (char.IsDigit(json[0]) || json[0] == '-')
|
||||
{
|
||||
if (json.Contains('.'))
|
||||
{
|
||||
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = int.TryParse(json, out int result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (json == "true")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (json == "false")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// handles json == "null" as well as invalid JSON
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members)
|
||||
where T : MemberInfo
|
||||
{
|
||||
Dictionary<string, T> nameToMember = new(StringComparer.OrdinalIgnoreCase);
|
||||
for (int i = 0; i < members.Length; i++)
|
||||
{
|
||||
/*T member = members[i];
|
||||
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
|
||||
continue;
|
||||
|
||||
string name = member.Name;
|
||||
if (member.IsDefined(typeof(DataMemberAttribute), true))
|
||||
{
|
||||
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
|
||||
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
|
||||
name = dataMemberAttribute.Name;
|
||||
}
|
||||
|
||||
nameToMember.Add(name, member);*/
|
||||
// The above code is not working with .Net framework 2.0, so we ignore these attributes for compatibility reasons:
|
||||
nameToMember.Add(members[i].Name, members[i]);
|
||||
}
|
||||
|
||||
return nameToMember;
|
||||
}
|
||||
|
||||
private static object ParseObject(Type type, string json)
|
||||
{
|
||||
object instance = FormatterServices.GetUninitializedObject(type);
|
||||
|
||||
// The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
|
||||
List<string> elems = Split(json);
|
||||
if (elems.Count % 2 != 0)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (!fieldInfoCache.TryGetValue(type, out Dictionary<string, FieldInfo> nameToField))
|
||||
{
|
||||
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
fieldInfoCache.Add(type, nameToField);
|
||||
}
|
||||
|
||||
if (!propertyInfoCache.TryGetValue(type, out Dictionary<string, PropertyInfo> nameToProperty))
|
||||
{
|
||||
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
|
||||
propertyInfoCache.Add(type, nameToProperty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < elems.Count; i += 2)
|
||||
{
|
||||
if (elems[i].Length <= 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = elems[i][1..^1];
|
||||
string value = elems[i + 1];
|
||||
|
||||
if (nameToField.TryGetValue(key, out FieldInfo fieldInfo))
|
||||
{
|
||||
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
|
||||
}
|
||||
else if (nameToProperty.TryGetValue(key, out PropertyInfo propertyInfo))
|
||||
{
|
||||
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user