299 lines
11 KiB
C#
299 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Web;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Globalization;
|
|
using Microsoft.VisualBasic.CompilerServices;
|
|
using System.Data;
|
|
|
|
namespace MESws
|
|
{
|
|
|
|
/// <summary>
|
|
/// 20210513 13871,Json相關擴充方法
|
|
/// </summary>
|
|
internal static class JsonSerializationExceptionHelper
|
|
{
|
|
public static JsonSerializationException Create(this JsonReader reader, string format, params object[] args)
|
|
{
|
|
IJsonLineInfo lineInfo = reader as IJsonLineInfo;
|
|
string path = reader == null ? null : reader.Path;
|
|
string message = string.Format(CultureInfo.InvariantCulture, format, args);
|
|
|
|
if (Convert.ToBoolean(!message.EndsWith(Environment.NewLine, StringComparison.Ordinal)))
|
|
{
|
|
message = message.Trim();
|
|
if (Convert.ToBoolean(!message.EndsWith(".", StringComparison.Ordinal)))
|
|
message += ".";
|
|
message += " ";
|
|
}
|
|
|
|
message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path);
|
|
if (lineInfo != null && lineInfo.HasLineInfo()) message += String.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition);
|
|
message += ".";
|
|
return new JsonSerializationException(Convert.ToString(message));
|
|
}
|
|
}
|
|
|
|
internal static class StringUtils
|
|
{
|
|
public static string FormatWith(this string format, IFormatProvider provider, object arg0)
|
|
{
|
|
return format.FormatWith(provider, new[] { arg0 });
|
|
}
|
|
|
|
private static string FormatWith(string format, IFormatProvider provider, params object[] args)
|
|
{
|
|
return string.Format(provider, format, args);
|
|
}
|
|
}
|
|
|
|
internal static class JsonReaderExtensions
|
|
{
|
|
public static void ReadAndAssert(this JsonReader reader)
|
|
{
|
|
if (reader == null)
|
|
throw new ArgumentNullException("reader");
|
|
|
|
if (!reader.Read())
|
|
{
|
|
throw reader.Create("Unexpected end when reading JSON.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 20210513 13871,Json DataTableConverter類別,自定義轉型方法,排除浮點數被轉成整數問題
|
|
/// </summary>
|
|
internal class TypeInferringDataTableConverter : Newtonsoft.Json.Converters.DataTableConverter
|
|
{
|
|
|
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
{
|
|
if (reader.TokenType == JsonToken.Null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
DataTable dt = existingValue as DataTable;
|
|
|
|
if (dt == null)
|
|
{
|
|
dt = objectType == typeof(DataTable) ? new DataTable() : (DataTable)Activator.CreateInstance(objectType);
|
|
}
|
|
|
|
if (reader.TokenType == JsonToken.PropertyName)
|
|
{
|
|
dt.TableName = Convert.ToString(reader.Value);
|
|
reader.ReadAndAssert();
|
|
|
|
if (reader.TokenType == JsonToken.Null)
|
|
{
|
|
return dt;
|
|
}
|
|
}
|
|
|
|
if (reader.TokenType != JsonToken.StartArray)
|
|
{
|
|
throw reader.Create("Unexpected JSON token when reading DataTable. Expected StartArray, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
|
|
}
|
|
|
|
reader.ReadAndAssert();
|
|
object ambiguousColumnTypes = new HashSet<string>();
|
|
|
|
while (reader.TokenType != JsonToken.EndArray)
|
|
{
|
|
CreateRow(reader, dt, serializer, (HashSet<string>)ambiguousColumnTypes);
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
return dt;
|
|
}
|
|
|
|
private static void CreateRow(JsonReader reader, DataTable dt, JsonSerializer serializer, HashSet<string> ambiguousColumnTypes)
|
|
{
|
|
var dr = dt.NewRow();
|
|
reader.ReadAndAssert();
|
|
|
|
while (reader.TokenType == JsonToken.PropertyName)
|
|
{
|
|
string columnName = Convert.ToString(reader.Value);
|
|
reader.ReadAndAssert();
|
|
DataColumn column = dt.Columns[columnName]; // 20211203 13871,DataTable內欄位如果是陣列、物件則不處理
|
|
|
|
if (column == null)
|
|
{
|
|
bool isAmbiguousType = false;
|
|
Type columnType = GetColumnDataType(reader, ref isAmbiguousType);
|
|
if (columnType == null) //20211203 13871,DataTable內欄位如果是陣列、物件則不處理
|
|
{
|
|
reader.ReadAndAssert();
|
|
continue;
|
|
}
|
|
column = new DataColumn(columnName, columnType);
|
|
dt.Columns.Add(column);
|
|
if (isAmbiguousType) ambiguousColumnTypes.Add(columnName);
|
|
}
|
|
else if (ambiguousColumnTypes.Contains(columnName))
|
|
{
|
|
bool isAmbiguousType = false;
|
|
Type newColumnType = GetColumnDataType(reader, ref isAmbiguousType);
|
|
if (!isAmbiguousType) ambiguousColumnTypes.Remove(columnName);
|
|
|
|
|
|
if (newColumnType != column.DataType)
|
|
{
|
|
column = ReplaceColumn(dt, column, newColumnType, serializer);
|
|
}
|
|
}
|
|
|
|
if (column.DataType == typeof(DataTable))
|
|
{
|
|
|
|
if (reader.TokenType == JsonToken.StartArray)
|
|
{
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
var nestedDt = new DataTable();
|
|
object nestedUnknownColumnTypes = new HashSet<string>();
|
|
|
|
while (reader.TokenType != JsonToken.EndArray)
|
|
{
|
|
CreateRow(reader, nestedDt, serializer, (HashSet<string>)nestedUnknownColumnTypes);
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
dr[columnName] = nestedDt;
|
|
}
|
|
else if (column.DataType.IsArray && column.DataType != typeof(byte[]))
|
|
{
|
|
|
|
if (reader.TokenType == JsonToken.StartArray)
|
|
{
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
var o = new List<object>();
|
|
|
|
while (reader.TokenType != JsonToken.EndArray)
|
|
{
|
|
o.Add(reader.Value);
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
var destinationArray = Array.CreateInstance(column.DataType.GetElementType(), o.Count);
|
|
Array.Copy(o.ToArray(), destinationArray, o.Count);
|
|
dr[columnName] = destinationArray;
|
|
}
|
|
else
|
|
{
|
|
var columnValue = reader.Value != null ? serializer.Deserialize(reader, column.DataType) ?? DBNull.Value : DBNull.Value;
|
|
dr[columnName] = columnValue;
|
|
}
|
|
|
|
reader.ReadAndAssert();
|
|
}
|
|
|
|
dr.EndEdit();
|
|
dt.Rows.Add(dr);
|
|
}
|
|
|
|
private static object RemapValue(object oldValue, Type newType, JsonSerializer serializer)
|
|
{
|
|
if (oldValue == null)
|
|
return null;
|
|
if (Convert.ToBoolean(Operators.ConditionalCompareObjectEqual(oldValue, DBNull.Value, false)))
|
|
return oldValue;
|
|
return JToken.FromObject(oldValue, serializer).ToObject(newType, serializer);
|
|
}
|
|
|
|
private static DataColumn ReplaceColumn(DataTable dt, DataColumn column, Type newColumnType, JsonSerializer serializer)
|
|
{
|
|
List<object> newValues = Enumerable.Range(0, dt.Rows.Count).Select(i => dt.Rows[i]).Select(r => RemapValue(r[column], newColumnType, serializer)).ToList();
|
|
int ordinal = column.Ordinal;
|
|
string name = column.ColumnName;
|
|
string @namespace = column.Namespace;
|
|
DataColumn newColumn = new DataColumn(Convert.ToString(name), newColumnType);
|
|
newColumn.Namespace = @namespace;
|
|
dt.Columns.Remove(column);
|
|
dt.Columns.Add(newColumn);
|
|
newColumn.SetOrdinal(ordinal);
|
|
|
|
for (int i = 0, loopTo = dt.Rows.Count - 1; i <= loopTo; i++)
|
|
dt.Rows[i][newColumn] = newValues[i];
|
|
|
|
return (DataColumn)newColumn;
|
|
}
|
|
|
|
private static Type GetColumnDataType(JsonReader reader, ref bool isAmbiguous)
|
|
{
|
|
var tokenType = reader.TokenType;
|
|
|
|
switch (tokenType)
|
|
{
|
|
case JsonToken.Integer:
|
|
{
|
|
isAmbiguous = false;
|
|
return Type.GetType("System.Decimal");
|
|
}
|
|
case JsonToken.Float:
|
|
{
|
|
isAmbiguous = false;
|
|
return Type.GetType("System.Decimal");
|
|
}
|
|
case JsonToken.Null:
|
|
case JsonToken.Undefined:
|
|
{
|
|
isAmbiguous = false;
|
|
return typeof(string);
|
|
}
|
|
case JsonToken.Boolean:
|
|
{
|
|
return Type.GetType("System.Boolean");
|
|
}
|
|
// 20211203 13871,DataTable內欄位如果是陣列、物件則不處理
|
|
case JsonToken.StartArray:
|
|
{
|
|
isAmbiguous = false;
|
|
int iCount = 1;
|
|
while (iCount > 0)
|
|
{
|
|
reader.ReadAndAssert();
|
|
var tokenType2 = reader.TokenType;
|
|
if (tokenType2 == JsonToken.StartArray)
|
|
iCount += 1;
|
|
if (tokenType2 == JsonToken.EndArray)
|
|
iCount -= 1;
|
|
}
|
|
return null;
|
|
}
|
|
case JsonToken.StartObject:
|
|
{
|
|
isAmbiguous = false;
|
|
int iCount = 1;
|
|
while (iCount > 0)
|
|
{
|
|
reader.ReadAndAssert();
|
|
var tokenType2 = reader.TokenType;
|
|
if (tokenType2 == JsonToken.StartObject)
|
|
iCount += 1;
|
|
if (tokenType2 == JsonToken.EndObject)
|
|
iCount -= 1;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
default:
|
|
{
|
|
isAmbiguous = false;
|
|
return reader.ValueType;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} |