Unity Json解析
JSONObject
http://wiki.unity3d.com/index.php?title=JSONObject
ISerializable.cs
using System;
namespace testJson.Json
{
public interface ISerializable
{
string ToJson();
ISerializable FromObject(object obj);
}
}
JsonParser.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace testJson.Json
{
public class JsonParser
{
private readonly string json;
private readonly StringBuilder sb = new StringBuilder();
private int index = 0;
private int jsonLength = -1;
private int nextBackslash = -1;
private JsonTokens lookAheadToken = JsonTokens.None;
private bool internStrings = false;
// This is effectively a constant, so we do not reset this static on game reload.
private static readonly JsonTokens[] jsonTokenMap = new JsonTokens[256];
// Minimize dictionary creation, use a pool for empty dictionaries.
#if SERVER
// The battle server requires thread-safety.
private readonly List<Dictionary<string, object>> pool =
new List<Dictionary<string, object>>();
#else
// The client is single-threaded and saves on many thousands of empty dictionary
// instance creations if we make this static.
private static List<Dictionary<string, object>> pool =
new List<Dictionary<string, object>>();
// Per team coding conventions, we must reset all statics on game reload.
public static void StaticReset()
{
pool.Clear();
}
#endif
static JsonParser()
{
for (int i = 0; i < 256; i++)
{
jsonTokenMap[i] = JsonTokens.None;
}
jsonTokenMap['{'] = JsonTokens.ObjectOpen;
jsonTokenMap['}'] = JsonTokens.ObjectClose;
jsonTokenMap['['] = JsonTokens.ArrayOpen;
jsonTokenMap[']'] = JsonTokens.ArrayClose;
jsonTokenMap[','] = JsonTokens.Comma;
jsonTokenMap['"'] = JsonTokens.String;
jsonTokenMap['0'] = JsonTokens.Number;
jsonTokenMap['1'] = JsonTokens.Number;
jsonTokenMap['2'] = JsonTokens.Number;
jsonTokenMap['3'] = JsonTokens.Number;
jsonTokenMap['4'] = JsonTokens.Number;
jsonTokenMap['5'] = JsonTokens.Number;
jsonTokenMap['6'] = JsonTokens.Number;
jsonTokenMap['7'] = JsonTokens.Number;
jsonTokenMap['8'] = JsonTokens.Number;
jsonTokenMap['9'] = JsonTokens.Number;
jsonTokenMap['-'] = JsonTokens.Number;
jsonTokenMap['+'] = JsonTokens.Number;
jsonTokenMap['.'] = JsonTokens.Number;
jsonTokenMap[':'] = JsonTokens.Colon;
jsonTokenMap['f'] = JsonTokens.False;
jsonTokenMap['t'] = JsonTokens.True;
jsonTokenMap['n'] = JsonTokens.Null;
}
public JsonParser(string json) : this(json, 0, false)
{
}
public JsonParser(string json, int startFrom) : this(json, startFrom, false)
{
}
public JsonParser(string json, int startFrom, bool internStrings)
{
this.json = json;
jsonLength = json == null ? 0 : json.Length;
index = startFrom;
this.internStrings = internStrings;
nextBackslash = 0; // Init to non-negative, so that the first find works.
FindNextBackslash();
}
public object Parse()
{
return jsonLength == 0 ? null : ParseValue();
}
private Dictionary<string, object> ParseObject()
{
Dictionary<string, object> result;
if (pool.Count == 0)
{
result = new Dictionary<string, object>();
}
else
{
int i = pool.Count - 1;
result = pool[i];
pool.RemoveAt(i);
}
lookAheadToken = JsonTokens.None;
for (;;)
{
JsonTokens token = LookAhead();
if (token == JsonTokens.Comma)
{
lookAheadToken = JsonTokens.None;
}
else if (token == JsonTokens.ObjectClose)
{
lookAheadToken = JsonTokens.None;
break;
}
else
{
string name = ParseString();
JsonTokens nextToken =
(lookAheadToken != JsonTokens.None) ? lookAheadToken : NextTokenCore();
lookAheadToken = JsonTokens.None;
if (nextToken != JsonTokens.Colon)
{
result.Clear();
break;
}
object value = ParseValue();
// NOTE: There is no need to check result.ContainsKey() because
// any collisions imply invalid JSON data and we'd want a throw
// in that case anyway.
result[name] = value;
}
}
// Null is the same as "empty dictionary". All user code must handle this.
// Before this change, over 65% of our 50,000 dictionary instances in the
// game were empty, and that's very wasteful of memory.
if (result.Count == 0)
{
pool.Add(result);
result = null;
}
return result;
}
private List<object> ParseArray()
{
var array = new List<object>();
lookAheadToken = JsonTokens.None;
while (true)
{
JsonTokens token = LookAhead();
if (token == JsonTokens.Comma)
{
lookAheadToken = JsonTokens.None;
}
else if (token == JsonTokens.ArrayClose)
{
lookAheadToken = JsonTokens.None;
array.Capacity = array.Count;
return array;
}
else
{
array.Add(ParseValue());
}
}
}
private object ParseValue()
{
switch (LookAhead())
{
case JsonTokens.Number:
return ParseNumber();
case JsonTokens.String:
return ParseString();
case JsonTokens.ObjectOpen:
return ParseObject();
case JsonTokens.ArrayOpen:
return ParseArray();
case JsonTokens.True:
lookAheadToken = JsonTokens.None;
return true;
case JsonTokens.False:
lookAheadToken = JsonTokens.None;
return false;
case JsonTokens.Null:
lookAheadToken = JsonTokens.None;
return null;
}
return null;
}
private void FindNextBackslash()
{
// Only find the next backslash if we didn't already find out that there are no
// more backslashes to find.
if (nextBackslash >= 0)
{
nextBackslash = json.IndexOf('\\', index);
}
}
private String NewString(String s)
{
return internStrings ? String.Intern(s) : s;
}
private String ParseString()
{
lookAheadToken = JsonTokens.None;
// Optimization: Common case, no escape sequences in the current string.
int endQuoteIndex = json.IndexOf('"', index);
if (endQuoteIndex < 0)
{
return null;
}
if (nextBackslash < 0 || nextBackslash > endQuoteIndex)
{
String s = json.Substring(index, endQuoteIndex - index);
index = endQuoteIndex + 1;
return NewString(s);
}
// If there's an escape sequence in this string, step through each character.
sb.Length = 0;
int runIndex = -1;
while (index < jsonLength)
{
var c = json[index++];
if (c == '"')
{
if (runIndex != -1)
{
if (sb.Length == 0)
{
FindNextBackslash();
return NewString(json.Substring(runIndex, index - runIndex - 1));
}
sb.Append(json.Substring(runIndex, index - runIndex - 1));
}
FindNextBackslash();
return NewString(sb.ToString());
}
if (c != '\\')
{
if (runIndex == -1)
{
runIndex = index - 1;
}
continue;
}
if (index == jsonLength)
{
break;
}
if (runIndex != -1)
{
sb.Append(json.Substring(runIndex, index - runIndex - 1));
runIndex = -1;
}
switch (json[index++])
{
case '"':
sb.Append('"');
break;
case '\\':
sb.Append('\\');
break;
case '/':
sb.Append('/');
break;
case 'b':
sb.Append('\b');
break;
case 'f':
sb.Append('\f');
break;
case 'n':
sb.Append('\n');
break;
case 'r':
sb.Append('\r');
break;
case 't':
sb.Append('\t');
break;
case 'u':
if (jsonLength - index < 4)
{
break;
}
// parse 32 bit hex
uint codePoint = ParseUnicode
(
json[index],
json[index + 1],
json[index + 2],
json[index + 3]
);
sb.Append((char)codePoint);
// skip 4 chars
index += 4;
break;
}
}
FindNextBackslash();
return null;
}
private uint ParseSingleChar(char c1, uint multipliyer)
{
uint p1 = 0;
if (c1 >= '0' && c1 <= '9')
{
p1 = (uint)(c1 - '0') * multipliyer;
}
else if (c1 >= 'A' && c1 <= 'F')
{
p1 = (uint)((c1 - 'A') + 10) * multipliyer;
}
else if (c1 >= 'a' && c1 <= 'f')
{
p1 = (uint)((c1 - 'a') + 10) * multipliyer;
}
return p1;
}
private uint ParseUnicode(char c1, char c2, char c3, char c4)
{
uint p1 = ParseSingleChar(c1, 0x1000);
uint p2 = ParseSingleChar(c2, 0x100);
uint p3 = ParseSingleChar(c3, 0x10);
uint p4 = ParseSingleChar(c4, 1);
return p1 + p2 + p3 + p4;
}
private String ParseNumber()
{
lookAheadToken = JsonTokens.None;
var startIndex = index - 1;
for (var c = json[index];
(c>='0' && c<='9') || c=='.' || c=='-' || c=='+' || c=='e' || c=='E';
c = json[index])
{
if (++index == jsonLength)
{
return null;
}
}
return NewString(json.Substring(startIndex, index - startIndex));
}
private JsonTokens LookAhead()
{
if (lookAheadToken != JsonTokens.None)
{
return lookAheadToken;
}
return lookAheadToken = NextTokenCore();
}
private JsonTokens NextTokenCore()
{
// Skip whitespace.
// Optimization: Anything before space in ASCII we can consider whitespace.
char c;
for (c = json[index]; c <= ' '; c = json[index])
{
if (++index == jsonLength)
{
return JsonTokens.None;
}
}
index++;
if (c >= 256)
{
return JsonTokens.None;
}
JsonTokens token = jsonTokenMap[c];
if (token >= JsonTokens.WordFirst)
{
// Find the end of the word.
do
{
// Optimization: Very lenient. Any word that starts with f means false,
// t -> true, n -> null. So, foobar -> false, totallyNotTrue -> true, and
// nope -> null.
c = json[index];
}
while (c >= 'a' && c <= 'z' && ++index < jsonLength);
}
return token;
}
}
}
JsonPropertyAttribute.cs
using System;
namespace testJson.Json
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)]
public class JsonPropertyAttribute : Attribute
{
public JsonPropertyAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
}
JsonSerializableAttribute.cs
using System;
namespace testJson.Json
{
public enum MappingType
{
Fields,
Properties
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
public class JsonSerializableAttribute : Attribute
{
public JsonSerializableAttribute(MappingType type)
{
this.Type = type;
}
public JsonSerializableAttribute()
: this(MappingType.Properties)
{
}
public MappingType Type { get; set; }
}
}
JsonTokens.cs
namespace testJson.Json
{
/// <summary>
/// Things like True, False and Null are special in that they represent "words"
/// in the json. We keep them in their own range for an optimzation.
/// </summary>
public enum JsonTokens
{
None,
ObjectOpen, ObjectClose,
ArrayOpen, ArrayClose,
Colon, Comma,
String, Number,
WordFirst = 100,
True = WordFirst,
False,
Null,
}
}
Serializer.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace testJson.Json
{
public class Serializer
{
private StringBuilder sb;
private bool first;
const string KEY_VALUE_QUOTED = "\"{0}\":\"{1}\"";
const string KEY_VALUE_UNQUOTED = "\"{0}\":{1}";
const string KEY_ONLY = "\"{0}\":";
const string OBJECT_START = "{";
const string OBJECT_END = "}";
const string ARRAY_START = "[";
const string ARRAY_END = "]";
const string COMMA = ",";
private static Dictionary<string, string> ESCAPE_MAPPING = new Dictionary<string, string>()
{
{"\"", "\\\""},
{"\\", @"\\"},
{"\a", @"\a"},
{"\b", @"\b"},
{"\f", @"\f"},
{"\n", @"\n"},
{"\r", @"\r"},
{"\t", @"\t"},
{"\v", @"\v"},
};
private static Regex ESCAPE_REGEX = new Regex(string.Join("|", ESCAPE_MAPPING.Keys.ToArray()));
private static string Escape(string s)
{
if (s != null)
{
return ESCAPE_REGEX.Replace(s, EscapeMatchEval);
}
return s;
}
private static string EscapeMatchEval(Match m)
{
return ESCAPE_MAPPING[m.Value];
}
public Serializer()
{
sb = new StringBuilder();
sb.Append(OBJECT_START);
first = true;
}
public static Serializer Start()
{
Serializer serializer = new Serializer();
return serializer;
}
public Serializer End()
{
sb.Append(OBJECT_END);
return this;
}
override public string ToString()
{
return sb.ToString();
}
public Serializer AddString(string key, string val)
{
return AddInternal<string>(key, Escape(val), KEY_VALUE_QUOTED);
}
public Serializer AddBool(string key, bool val)
{
// C# returns True/False instead of true/false
return Add<string>(key, val.ToString().ToLower());
}
public Serializer Add<T>(string key, T val)
{
return AddInternal<T>(key, val, KEY_VALUE_UNQUOTED);
}
private Serializer AddInternal<T>(string key, T val, string format)
{
AppendComma(first);
sb.AppendFormat(format, Escape(key), val);
first = false;
return this;
}
public Serializer AddObject<T>(string key, T val) where T : ISerializable
{
return Add(key, val.ToJson());
}
public Serializer AddArray<T>(string key, List<T> values) where T : ISerializable
{
AppendComma(first);
sb.AppendFormat(KEY_ONLY, Escape(key));
sb.Append(ARRAY_START);
bool arrayFirst = true;
foreach (T serializable in values)
{
AppendComma(arrayFirst);
Add(serializable);
arrayFirst = false;
}
sb.Append(ARRAY_END);
first = false;
return this;
}
public Serializer AddDictionary<T>(string key, Dictionary<string, T> values)
{
AppendComma(first);
sb.AppendFormat(KEY_ONLY, Escape(key));
sb.Append(OBJECT_START);
bool isString = typeof(T) == typeof(string);
// Reset first for this particular object
first = true;
foreach (KeyValuePair<string, T> pair in values)
{
if (isString)
{
if (pair.Value != null)
{
AddString(pair.Key, pair.Value.ToString());
}
else
{
AddString(pair.Key, null);
}
}
else
{
Add<T>(pair.Key, pair.Value);
}
}
// Set it to false again
first = false;
sb.Append(OBJECT_END);
return this;
}
public Serializer AddArrayOfPrimitives<T>(string key, List<T> values)
{
AppendComma(first);
sb.AppendFormat(KEY_ONLY, Escape(key));
sb.Append(ARRAY_START);
bool arrayFirst = true;
bool isString = typeof(T) == typeof(string);
foreach (T serializable in values)
{
AppendComma(arrayFirst);
if (isString)
{
AddQuoted(serializable.ToString());
}
else
{
Add(serializable.ToString());
}
arrayFirst = false;
}
sb.Append(ARRAY_END);
first = false;
return this;
}
private void Add(ISerializable val)
{
Add(val.ToJson());
}
private void Add(string val)
{
sb.Append(val);
}
private void AddQuoted(string val)
{
sb.Append('"');
sb.Append(Escape(val));
sb.Append('"');
}
private void AppendComma(bool first)
{
if (!first)
{
sb.Append(COMMA);
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using testJson.Json;
//using UnityEngine;
namespace testJson
{
public class Player
{
public string name;
public int age;
}
class Program
{
static void Main(string[] args)
{
string json = "{\"Players\":[{ \"name\": \"guodong1\", \"age\": 23},{ \"name\": \"guodong2\", \"age\": 24}]}";
object obj = new JsonParser(json).Parse();
Dictionary<string, object> dict = obj as Dictionary<string, object>;
if (dict.ContainsKey("Players"))
{
List<object> players = dict["Players"] as List<object>;
foreach (var p in players)
{
Player player = new Player();
Dictionary<string, object> playerDict = p as Dictionary<string, object>;
if (playerDict.ContainsKey("name"))
{
player.name = playerDict["name"].ToString();
Console.WriteLine("player name = " + player.name);
}
if (playerDict.ContainsKey("age"))
{
player.age = int.Parse(playerDict["age"].ToString());
Console.WriteLine("player age = " + player.age);
}
}
}
}
}
}
运行结果: