using FastReport.Data.JsonConnection.JsonParser; using FastReport.Utils; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; namespace FastReport.Data.JsonConnection { /// /// JsonTableDataSource present a json array object /// public class JsonTableDataSource : TableDataSource { #region Private Fields private JsonArray _json; private bool updateSchema; private bool simpleStructure; private string tableData; #endregion Private Fields #region Public Properties /// /// Gets or sets value for force update schema on init schema /// [Browsable(false)] public bool UpdateSchema { get { return updateSchema; } set { updateSchema = value; } } /// /// Get or sets simplify mode for array types /// [Browsable(false)] public bool SimpleStructure { get { return simpleStructure; } set { simpleStructure = value; } } /// [Browsable(false)] public override string TableData { get { if (String.IsNullOrEmpty(tableData)) { tableData = Json.ToString(); } return tableData; } set { tableData = value; } } #endregion Public Properties #region Internal Properties internal JsonArray Json { get { if (_json == null) { if (StoreData && !String.IsNullOrEmpty(tableData)) { _json = JsonBase.FromString(tableData) as JsonArray; } else { _json = GetJson(Parent, this) as JsonArray; } if (_json == null) _json = new JsonArray(); } return _json; } set { _json = value; } } #endregion Internal Properties #region Private Properties private int CurrentIndex { get { if (currentRow is int) return (int)currentRow; if (CurrentRowNo < Rows.Count) { object result = Rows[CurrentRowNo]; if (result is int) return (int)result; } return CurrentRowNo; } } #endregion Private Properties #region Public Constructors /// public JsonTableDataSource() { DataType = typeof(JsonArray); } #endregion Public Constructors #region Public Methods /// public override void InitializeComponent() { base.InitializeComponent(); Json = null; } /// public override void InitSchema() { if (Columns.Count == 0 || UpdateSchema && !StoreData) { if (Connection is JsonDataSourceConnection) { JsonDataSourceConnection con = Connection as JsonDataSourceConnection; InitSchema(this, con.JsonSchema, con.SimpleStructure); } } UpdateSchema = false; } /// public override void LoadData(ArrayList rows) { Json = null; // JSON is calculated property, no problem with null if (rows != null && Json != null) { rows.Clear(); int count = Json.Count; for (int i = 0; i < count; i++) { rows.Add(i); } } } #endregion Public Methods #region Internal Methods internal static object GetJsonBaseByColumn(Base parentColumn, Column column) { if (parentColumn is JsonTableDataSource) { JsonTableDataSource jsonTableDataSource = parentColumn as JsonTableDataSource; if (jsonTableDataSource.SimpleStructure) { if (!String.IsNullOrEmpty(column.PropName)) { var obj = (parentColumn as JsonTableDataSource).Json[(parentColumn as JsonTableDataSource).CurrentIndex]; return (obj as JsonBase)[column.PropName]; } } else { switch (column.PropName) { case "item": return (parentColumn as JsonTableDataSource).Json[(parentColumn as JsonTableDataSource).CurrentIndex]; } JsonTableDataSource source = column as JsonTableDataSource; return source.Json; } } if (parentColumn is Column && !String.IsNullOrEmpty(column.PropName)) { object json = GetJsonBaseByColumn(parentColumn.Parent, parentColumn as Column); if (json is JsonBase) { return (json as JsonBase)[column.PropName]; } } return null; } internal static object GetValueByColumn(Base parentColumn, Column column) { if (parentColumn is JsonTableDataSource) { switch (column.PropName) { case "index": return (parentColumn as JsonTableDataSource).CurrentIndex; case "array": return (parentColumn as JsonTableDataSource).Json; } } object json = GetJsonBaseByColumn(parentColumn, column); return json; } internal static void InitSchema(Column table, JsonSchema schema, bool simpleStructure) { List saveColumns = new List(); switch (schema.Type) { case "object": foreach (KeyValuePair kv in schema.Properties) { if (kv.Value.Type == "object") { Column c = new Column(); c.Name = kv.Key; c.Alias = kv.Key; c.PropName = kv.Key; c.DataType = kv.Value.DataType; c = UpdateColumn(table, c, saveColumns); InitSchema(c, kv.Value, simpleStructure); } else if (kv.Value.Type == "array") { Column c = new JsonTableDataSource(); c.Name = kv.Key; c.Alias = kv.Key; c.PropName = kv.Key; c.DataType = kv.Value.DataType; c = UpdateColumn(table, c, saveColumns); InitSchema(c, kv.Value, simpleStructure); } else { Column c = new Column(); c.Name = kv.Key; c.Alias = kv.Key; c.PropName = kv.Key; c.DataType = kv.Value.DataType; c.SetBindableControlType(c.DataType); UpdateColumn(table, c, saveColumns); } } break; case "array": JsonSchema items = schema.Items; bool simpleArray = false; if (table is JsonTableDataSource) { JsonTableDataSource jsonTableDataSource = table as JsonTableDataSource; simpleArray = jsonTableDataSource.SimpleStructure = simpleStructure & items.Type == "object"; } if (simpleArray) { // remake schema in simplify mode InitSchema(table, items, simpleStructure); // and return, no need to clear column data // in this case this method has no control to columns return; } { Column c = new Column(); c.Name = "index"; c.Alias = "index"; c.PropName = "index"; c.DataType = typeof(int); UpdateColumn(table, c, saveColumns); } { Column c; bool iSchema = false; if (items.Type == "object") { c = new Column(); iSchema = true; } else if (items.Type == "array") { c = new JsonTableDataSource(); iSchema = true; } else { c = new Column(); } c.Name = "item"; c.Alias = "item"; c.PropName = "item"; c.DataType = items.DataType; c = UpdateColumn(table, c, saveColumns); if (iSchema) InitSchema(c, items, simpleStructure); } { Column c = new Column(); c.Name = "array"; c.Alias = "array"; c.PropName = "array"; c.DataType = typeof(JsonBase); UpdateColumn(table, c, saveColumns); } break; } for (int i = 0; i < table.Columns.Count; i++) { if (!(table.Columns[i].Calculated || saveColumns.Contains(table.Columns[i]))) { table.Columns.RemoveAt(i); i--; } } } internal object GetJson(Base parentColumn, Column column) { if(parentColumn is IJsonProviderSourceConnection) { return (parentColumn as IJsonProviderSourceConnection).GetJson(this); } //if (parentColumn is ESDataSourceConnection) // parentColumn = (parentColumn as ESDataSourceConnection).GetParentJTDSByName(column.Name); if (parentColumn is JsonTableDataSource) { //if (parentColumn.Parent is ESDataSourceConnection) // parentColumn.Parent = (parentColumn.Parent as ESDataSourceConnection).GetParentJTDSByName(parentColumn.Name); // Here it’s completely fail, // I commented out the code with ElasticSearch, // when you bring ElasticSearch into a separate plugin, // then this will need to be corrected JsonTableDataSource source = parentColumn as JsonTableDataSource; if (source.SimpleStructure) { object parentJson = source.Json[source.CurrentRowNo]; if (parentJson is JsonBase && !String.IsNullOrEmpty(column.PropName)) { return (parentJson as JsonBase)[column.PropName]; } } else { return source.Json[source.CurrentRowNo] as object; } } else if (parentColumn is Column) { object parentJson = GetJson(parentColumn.Parent, parentColumn as Column); if (parentJson is JsonBase && !String.IsNullOrEmpty(column.PropName)) { return (parentJson as JsonBase)[column.PropName]; } } return null; } #endregion Internal Methods #region Protected Methods /// protected override object GetValue(Column column) { return GetValueByColumn(column.Parent, column); } /// protected override object GetValue(string alias) { // TODO TEST Column column = this; string[] colAliases = alias.Split('.'); foreach (string colAlias in colAliases) { column = column.Columns.FindByAlias(colAlias); if (column == null) return null; } return GetValueByColumn(column.Parent, column); } #endregion Protected Methods #region Private Methods private static Column UpdateColumn(Column table, Column c, List list) { foreach (Column child in table.Columns) { if (child.PropName == c.PropName) { child.DataType = c.DataType; list.Add(child); return child; } } table.AddChild(c); list.Add(c); return c; } #endregion Private Methods /// public override void Serialize(FRWriter writer) { base.Serialize(writer); if (SimpleStructure) { writer.WriteBool("SimpleStructure", SimpleStructure); } } } }