private static TypedDoc toTypedDoc(Type type, DocReadOptions?options, JsonDataMap jsonMap, ref string field, bool fromUI) { var toAllocate = type; var customHandler = JsonHandlerAttribute.TryFind(type); if (customHandler != null) { var castResult = customHandler.TypeCastOnRead(jsonMap, type, fromUI, options ?? DocReadOptions.BindByCode); if (castResult.Outcome >= JsonHandlerAttribute.TypeCastOutcome.ChangedTargetType) { toAllocate = castResult.ToType.IsOfType(type, "Changed type must be a subtype of specific document type"); } if (castResult.Outcome == JsonHandlerAttribute.TypeCastOutcome.ChangedSourceValue) { jsonMap = (castResult.Value as JsonDataMap).NonNull("Changed source value must be of JsonDataMap type for root data documents"); } else if (castResult.Outcome == JsonHandlerAttribute.TypeCastOutcome.HandledCast) { return(castResult.Value.ValueIsOfType(type, "HandledCast must return TypedDoc for root data documents") as TypedDoc); } } if (toAllocate.IsAbstract) { throw new JSONDeserializationException(StringConsts.JSON_DESERIALIZATION_ABSTRACT_TYPE_ERROR.Args(toAllocate.Name, nameof(JsonHandlerAttribute))); } var doc = (TypedDoc)SerializationUtils.MakeNewObjectInstance(toAllocate); toDoc(doc, options.HasValue ? options.Value : DocReadOptions.BindByCode, jsonMap, ref field, fromUI); return(doc); }
/// <summary> /// Converts JSONMap into supplied row instance. /// The extra data found in JSON map will be placed in AmorphousData dictionary if the row implements IAmorphousData, discarded otherwise. /// Note: This method provides "the best match" and does not guarantee that all data will/can be converted from JSON, i.e. /// it can only convert one dimensional arrays and Lists of either primitive or TypeRow-derived entries /// </summary> /// <param name="doc">Data document instance to convert into</param> /// <param name="jsonMap">JSON data to convert into row</param> /// <param name="fromUI">When true indicates that data came from UI, hence NonUI-marked fields should be skipped. True by default</param> /// <param name="options">Used for backend name matching or null (any target)</param> public static void ToDoc(Doc doc, JsonDataMap jsonMap, bool fromUI = true, DocReadOptions?options = null) { if (doc == null || jsonMap == null) { throw new JSONDeserializationException(StringConsts.ARGUMENT_ERROR + "JSONReader.ToDoc(doc|jsonMap=null)"); } var field = ""; try { var tDoc = doc.GetType(); var customHandler = JsonHandlerAttribute.TryFind(tDoc); if (customHandler != null) { var castResult = customHandler.TypeCastOnRead(jsonMap, tDoc, fromUI, options ?? DocReadOptions.BindByCode); //only reacts to ChangeSource because this method works on pre-allocated doc if (castResult.Outcome == JsonHandlerAttribute.TypeCastOutcome.ChangedSourceValue) { jsonMap = (castResult.Value as JsonDataMap).NonNull("Changed source value must be of JsonDataMap type for root data documents"); } } toDoc(doc, options.HasValue ? options.Value : DocReadOptions.BindByCode, jsonMap, ref field, fromUI); } catch (Exception error) { throw new JSONDeserializationException("JSONReader.ToDoc(doc, jsonMap) error in '{0}': {1}".Args(field, error.ToMessageWithType()), error); } }
private static bool setOneField(Doc doc, Schema.FieldDef def, object fv, bool fromUI, DocReadOptions options) { var fieldCustomHandler = JsonHandlerAttribute.TryFind(def.MemberInfo); var converted = cast(fv, def.Type, fromUI, options, fieldCustomHandler); if (converted != null) { doc.SetFieldValue(def, converted); return(true); } return(false); }//setOneField
}//setOneField //Returns non null on success; may return null for collection sub-element in which case null=null and does not indicate failure private static object cast(object v, Type toType, bool fromUI, DocReadOptions options, JsonHandlerAttribute fieldCustomHandler = null) { //See #264 - the collection inability to cast has no amorphous data so it MUST throw //used only for collections inner calls if (v == null) { return(null); } var customHandler = fieldCustomHandler ?? JsonHandlerAttribute.TryFind(toType); if (customHandler != null) { var castResult = customHandler.TypeCastOnRead(v, toType, fromUI, options); if (castResult.Outcome >= JsonHandlerAttribute.TypeCastOutcome.ChangedTargetType) { toType = castResult.ToType; } if (castResult.Outcome == JsonHandlerAttribute.TypeCastOutcome.ChangedSourceValue) { v = castResult.Value; } else if (castResult.Outcome == JsonHandlerAttribute.TypeCastOutcome.HandledCast) { return(castResult.Value); } } //object goes as is if (toType == typeof(object)) { return(v); } //IJSONDataObject if (toType == typeof(IJsonDataObject)) { if (v is IJsonDataObject) { return(v); //goes as is } if (v is string s) //string containing embedded JSON { var jo = s.JsonToDataObject(); return(jo); } } //IJSONDataMap if (toType == typeof(JsonDataMap)) { if (v is JsonDataMap) { return(v); //goes as is } if (v is string s) //string containing embedded JSON { var jo = s.JsonToDataObject() as JsonDataMap; return(jo); } } //IJSONDataArray if (toType == typeof(JsonDataArray)) { if (v is JsonDataArray) { return(v); //goes as is } if (v is string s) //string containing embedded JSON { var jo = s.JsonToDataObject() as JsonDataArray; return(jo); } } var nntp = toType; if (nntp.IsGenericType && nntp.GetGenericTypeDefinition() == typeof(Nullable <>)) { nntp = toType.GetGenericArguments()[0]; } //20191217 DKh if (nntp == typeof(DateTime)) { if (options.LocalDates) { var d = v.AsDateTime(System.Globalization.DateTimeStyles.AssumeLocal); return(d); } else //UTC (the default) { var d = v.AsDateTime(System.Globalization.DateTimeStyles.AssumeUniversal | System.Globalization.DateTimeStyles.AdjustToUniversal); return(d); } } //20191217 DKh //Custom JSON Readable (including config) if (typeof(IJsonReadable).IsAssignableFrom(nntp) || typeof(IConfigSectionNode).IsAssignableFrom(nntp)) { var toAllocate = nntp; //Configuration requires special handling because nodes do not exist as independent entities and there //is master/detail relationship between them if (toAllocate == typeof(Configuration) || toAllocate == typeof(ConfigSectionNode) || toAllocate == typeof(IConfigSectionNode)) { toAllocate = typeof(MemoryConfiguration); } if (toAllocate.IsAbstract) { throw new JSONDeserializationException(StringConsts.JSON_DESERIALIZATION_ABSTRACT_TYPE_ERROR.Args(toAllocate.Name, nameof(JsonHandlerAttribute))); } var newval = SerializationUtils.MakeNewObjectInstance(toAllocate) as IJsonReadable; var got = newval.ReadAsJson(v, fromUI, options);//this may re-allocate the result based of newval if (!got.match) { return(null); } if (typeof(IConfigSectionNode).IsAssignableFrom(nntp)) { return((got.self as Configuration)?.Root); } return(got.self); } //byte[] direct assignment w/o copies if (nntp == typeof(byte[]) && v is byte[] passed) { return(passed); } //field def = [] if (toType.IsArray) { var fvseq = v as IEnumerable; if (fvseq == null) { return(null); //can not set non enumerable into array } var arr = fvseq.Cast <object>().Select(e => cast(e, toType.GetElementType(), fromUI, options, fieldCustomHandler)).ToArray(); var newval = Array.CreateInstance(toType.GetElementType(), arr.Length); for (var i = 0; i < newval.Length; i++) { newval.SetValue(arr[i], i); } return(newval); } //field def = List<t> if (toType.IsGenericType && toType.GetGenericTypeDefinition() == typeof(List <>)) { var fvseq = v as IEnumerable; if (fvseq == null) { return(false); //can not set non enumerable into List<t> } var arr = fvseq.Cast <object>().Select(e => cast(e, toType.GetGenericArguments()[0], fromUI, options, fieldCustomHandler)).ToArray(); var newval = SerializationUtils.MakeNewObjectInstance(toType) as IList; for (var i = 0; i < arr.Length; i++) { newval.Add(arr[i]); } return(newval); } //last resort try { return(StringValueConversion.AsType(v.ToString(), toType, false)); } catch { return(null);//the value could not be converted, and is going to go into amorphous bag if it is enabled } }