aliyun-net-sdk-core/Reader/JsonReader.cs (369 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Aliyun.Acs.Core.Utils;
namespace Aliyun.Acs.Core.Reader
{
public class JsonReader : IReader
{
private const int FIRST_POSITION = 0;
private const int CURRENT_POSITION = 1;
private const int NEXT_POSITION = 2;
private static readonly object ARRAY_END_TOKEN = new object();
private static readonly object OBJECT_END_TOKEN = new object();
private static readonly object COMMA_TOKEN = new object();
private static readonly object COLON_TOKEN = new object();
private static readonly Dictionary<char, char> escapes = new Dictionary<char, char>
{
{'\\', '\\'},
{'/', '/'},
{'"', '"'},
{'t', '\t'},
{'n', '\n'},
{'r', '\r'},
{'b', '\b'},
{'f', '\f'}
};
private readonly Dictionary<string, string> map = new Dictionary<string, string>();
private readonly StringBuilder sb = new StringBuilder();
private char c;
private CharEnumerator ct;
private object token;
public Dictionary<string, string> Read(string response, string endpoint)
{
return Read(response.GetEnumerator(), endpoint);
}
public Dictionary<string, string> ReadForHideArrayItem(string response, string endpoint)
{
return ReadForHideArrayItem(response.GetEnumerator(), endpoint);
}
public Dictionary<string, string> Read(CharEnumerator ci, string endpoint)
{
ct = ci;
NextChar();
ReadJson(endpoint);
return map;
}
public Dictionary<string, string> ReadForHideArrayItem(CharEnumerator ci, string endpoint)
{
ct = ci;
NextChar();
ReadJsonForHideArrayItem(endpoint);
return map;
}
private object ReadJson(string baseKey)
{
SkipWhiteSpace();
var ch = c;
NextChar();
switch (ch)
{
case '{':
ProcessObject(baseKey);
break;
case '}':
token = OBJECT_END_TOKEN;
break;
case '[':
if (c == '"')
{
ProcessList(baseKey);
break;
}
else
{
ProcessArray(baseKey);
break;
}
case ']':
token = ARRAY_END_TOKEN;
break;
case '"':
token = ProcessString();
break;
case ',':
token = COMMA_TOKEN;
break;
case ':':
token = COLON_TOKEN;
break;
case 't':
NextChar();
NextChar();
NextChar();
token = true;
break;
case 'n':
NextChar();
NextChar();
NextChar();
token = null;
break;
case 'f':
NextChar();
NextChar();
NextChar();
NextChar();
token = false;
break;
default:
// c = ct.previous();
if (char.IsDigit(ch) || ch == '-')
{
token = ProcessNumber(ch);
}
break;
}
return token;
}
private object ReadJsonForHideArrayItem(string baseKey)
{
SkipWhiteSpace();
var ch = c;
NextChar();
switch (ch)
{
case '{':
ProcessObjectForHideArrayItem(baseKey);
break;
case '}':
token = OBJECT_END_TOKEN;
break;
case '[':
if (c == '"')
{
ProcessList(baseKey);
break;
}
else
{
ProcessArrayForHideArrayItem(baseKey);
break;
}
case ']':
token = ARRAY_END_TOKEN;
break;
case '"':
token = ProcessString();
break;
case ',':
token = COMMA_TOKEN;
break;
case ':':
token = COLON_TOKEN;
break;
case 't':
NextChar();
NextChar();
NextChar();
token = true;
break;
case 'n':
NextChar();
NextChar();
NextChar();
token = null;
break;
case 'f':
NextChar();
NextChar();
NextChar();
NextChar();
token = false;
break;
default:
// c = ct.previous();
if (char.IsDigit(ch) || ch == '-')
{
token = ProcessNumber(ch);
}
break;
}
return token;
}
private void ProcessObject(string baseKey)
{
var key = baseKey + "." + ReadJson(baseKey);
while (token != OBJECT_END_TOKEN)
{
ReadJson(key);
if (token != OBJECT_END_TOKEN)
{
var obj = ReadJson(key);
if (obj is string || obj is bool || obj is double)
{
DictionaryUtil.Add(map, key, obj.ToString());
}
if (ReadJson(key) == COMMA_TOKEN)
{
key = ReadJson(key).ToString();
key = baseKey + "." + key;
}
}
}
}
private void ProcessObjectForHideArrayItem(string baseKey)
{
var key = baseKey + "." + ReadJsonForHideArrayItem(baseKey);
while (token != OBJECT_END_TOKEN)
{
ReadJsonForHideArrayItem(key);
if (token != OBJECT_END_TOKEN)
{
var obj = ReadJsonForHideArrayItem(key);
if (obj is string || obj is bool || obj is double)
{
DictionaryUtil.Add(map, key, obj.ToString());
}
if (ReadJsonForHideArrayItem(key) == COMMA_TOKEN)
{
key = ReadJsonForHideArrayItem(key).ToString();
key = baseKey + "." + key;
}
}
}
}
private void ProcessList(string baseKey)
{
var value = ReadJson(baseKey);
var index = 0;
while (token != ARRAY_END_TOKEN)
{
var key = TrimFromLast(baseKey, ".") + "[" + index++ + "]";
DictionaryUtil.Add(map, key, value.ToString());
if (ReadJson(baseKey) == COMMA_TOKEN)
{
value = ReadJson(baseKey);
}
}
DictionaryUtil.Add(map, TrimFromLast(baseKey, ".") + ".Length", index.ToString());
}
private void ProcessArray(string baseKey)
{
var index = 0;
var preKey = baseKey.Substring(0, baseKey.LastIndexOf("."));
var key = preKey + "[" + index + "]";
var value = ReadJson(key);
while (token != ARRAY_END_TOKEN)
{
DictionaryUtil.Add(map, preKey + ".Length", (index + 1).ToString());
if (value is string)
{
DictionaryUtil.Add(map, key, value.ToString());
}
if (ReadJson(baseKey) == COMMA_TOKEN)
{
key = preKey + "[" + ++index + "]";
value = ReadJson(key);
}
}
}
private void ProcessArrayForHideArrayItem(string baseKey)
{
var index = 0;
var preKey = baseKey;
var key = preKey + "[" + index + "]";
var value = ReadJsonForHideArrayItem(key);
while (token != ARRAY_END_TOKEN)
{
DictionaryUtil.Add(map, preKey + ".Length", (index + 1).ToString());
if (value is string)
{
DictionaryUtil.Add(map, key, value.ToString());
}
if (ReadJsonForHideArrayItem(baseKey) == COMMA_TOKEN)
{
key = preKey + "[" + ++index + "]";
value = ReadJsonForHideArrayItem(key);
}
}
}
private object ProcessNumber(char preChar)
{
sb.Clear();
sb.Append(preChar);
if ('-' == c)
{
AddChar();
}
AddDigits();
if ('.' == c)
{
AddChar();
AddDigits();
}
if ('e' == c || 'E' == c)
{
AddChar();
if ('+' == c || '-' == c)
{
AddChar();
}
AddDigits();
}
return sb.ToString();
}
private void AddDigits()
{
while (char.IsDigit(c))
{
AddChar();
}
}
private void SkipWhiteSpace()
{
while (char.IsWhiteSpace(c))
{
NextChar();
}
}
private char? NextChar()
{
if (ct.MoveNext())
{
c = ct.Current;
return c;
}
return null;
}
private object ProcessString()
{
sb.Clear();
while (c != '"')
{
if (c == '\\')
{
NextChar();
char value;
if (escapes.TryGetValue(c, out value))
{
AddChar(value);
}
}
else
{
AddChar();
}
}
NextChar();
return sb.ToString();
}
private void AddChar(char ch)
{
sb.Append(ch);
NextChar();
}
private void AddChar()
{
AddChar(c);
}
public static string TrimFromLast(string str, string stripString)
{
var pos = str.LastIndexOf(stripString);
if (pos > -1)
{
return str.Substring(0, pos);
}
return str;
}
}
}