public object ParseString(XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsNoString()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains string value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } parseStack.Push("string"); try { return(node.FirstChild == null ? string.Empty : node.FirstChild.Value); } finally { parseStack.Pop(); } }
public object ParseBoolean(XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsNoBoolean()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains boolean value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } parseStack.Push("boolean"); try { var textbool = node.FirstChild.Value; if (!bool.TryParse(textbool, out var parseResult)) { if (!textbool.Equals("0") && !textbool.Equals("1")) { throw new XmlRpcInvalidXmlRpcException($"reponse contains invalid boolean value '{textbool}' " + parseStack.Dump()); } parseResult = textbool.Equals("1"); } return(valueType == typeof(XmlRpcBoolean) ? new XmlRpcBoolean(parseResult) : (object)parseResult); } finally { parseStack.Pop(); } }
public object ParseBase64(XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsNoByteArray()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains base64 value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } parseStack.Push("base64"); try { if (node.FirstChild == null) { return(new byte[0]); } var base64String = node.FirstChild.Value; var buffer = new Span <byte>(); if (!Convert.TryFromBase64String(base64String, buffer, out _)) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains invalid base64 value " + parseStack.Dump()); } return(buffer.ToArray()); } finally { parseStack.Pop(); } }
public object ParseLong(XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsNoLong()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains i8 value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } parseStack.Push("i8"); try { var valueNode = node.FirstChild; if (valueNode == null) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains invalid i8 element " + parseStack.Dump()); } var strValue = valueNode.Value; if (!long.TryParse(strValue, out var parseResult)) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains invalid i8 value " + parseStack.Dump()); } return(parseResult); } finally { parseStack.Pop(); } }
public object ParseDouble(XmlNode node, Type ValueType, ParseStack parseStack) { if (ValueType.IsNoDouble()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains double value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(ValueType) + " expected " + parseStack.Dump()); } parseStack.Push("double"); try { if (!double.TryParse(node.FirstChild.Value, out var parseResult)) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains invalid double value " + parseStack.Dump()); } return(ValueType == typeof(XmlRpcDouble) ? new XmlRpcDouble(parseResult) : (object)parseResult); } finally { parseStack.Pop(); } }
public object ParseDateTime(XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsNoDateTime()) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains dateTime.iso8601 value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } parseStack.Push("dateTime"); try { var child = node.FirstChild; if (child == null) { if (_config.MapEmptyDateTimeToMinValue()) { return(DateTime.MinValue); } else { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains empty dateTime value " + parseStack.Dump()); } } var datestring = child.Value; if (!DateTime8601.TryParseDateTime8601(datestring, out var retVal)) { if (_config.MapZerosDateTimeToMinValue() && datestring.StartsWith("0000") && (datestring == "00000000T00:00:00" || datestring == "0000-00-00T00:00:00Z" || datestring == "00000000T00:00:00Z" || datestring == "0000-00-00T00:00:00")) { retVal = DateTime.MinValue; } else { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains invalid dateTime value " + parseStack.Dump()); } } return(valueType == typeof(XmlRpcDateTime) ? new XmlRpcDateTime(retVal) : (object)retVal); } finally { parseStack.Pop(); } }
object ParseValue(XmlNode node, Type ValueType, ParseStack parseStack, out Type ParsedType, out Type ParsedArrayType) { var parser = new XmlSystemTypeParser(_config); ParsedType = null; ParsedArrayType = null; // if suppplied type is System.object then ignore it because // if doesn't provide any useful information (parsing methods // expect null in this case) Type valType = ValueType; if (valType != null && valType.BaseType == null) { valType = null; } object retObj; if (node == null) { retObj = ""; } else if (node is XmlText || node is XmlWhitespace) { if (valType != null && valType != typeof(string)) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains implicit string value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valType) + " expected " + parseStack.Dump()); } retObj = node.Value; } else { if (node.Name == "array") { retObj = ParseArray(node, valType, parseStack); } else if (node.Name == "base64") { retObj = parser.ParseBase64(node, valType, parseStack); } else if (node.Name == "struct") { // if we don't know the expected class type then we must // parse the XML-RPC class as an instance of XmlRpcStruct if (valType != null && valType != typeof(XmlRpcStruct) && !valType.IsSubclassOf(typeof(XmlRpcStruct))) { retObj = ParseStruct(node, valType, parseStack); } else { retObj = ParseHashtable(node, parseStack); } } else if (node.Name == "i4" || node.Name == "int") // integer has two representations in XML-RPC spec { retObj = parser.ParseInt(node, valType, parseStack); ParsedType = typeof(int); ParsedArrayType = typeof(int[]); } else if (node.Name == "i8") { retObj = parser.ParseLong(node, valType, parseStack); ParsedType = typeof(long); ParsedArrayType = typeof(long[]); } else if (node.Name == "string") { retObj = parser.ParseString(node, valType, parseStack); ParsedType = typeof(string); ParsedArrayType = typeof(string[]); } else if (node.Name == "boolean") { retObj = parser.ParseBoolean(node, valType, parseStack); ParsedType = typeof(bool); ParsedArrayType = typeof(bool[]); } else if (node.Name == "double") { retObj = parser.ParseDouble(node, valType, parseStack); ParsedType = typeof(double); ParsedArrayType = typeof(double[]); } else if (node.Name == "dateTime.iso8601") { retObj = parser.ParseDateTime(node, valType, parseStack); ParsedType = typeof(DateTime); ParsedArrayType = typeof(DateTime[]); } else { throw new XmlRpcInvalidXmlRpcException("Invalid value element: <" + node.Name + ">"); } } return(retObj); }
void ReportMissingMembers(Type valueType, Hashtable names, ParseStack parseStack) { var sb = new StringBuilder(); var errorCount = 0; var sep = string.Empty; foreach (string key in names.Keys) { var memberAction = AttributeHelper.MemberMappingAction(valueType, key, MappingAction.Error); if (memberAction == MappingAction.Error) { sb.Append(sep); sb.Append(key); sep = " "; errorCount++; } } if (errorCount == 0) { return; } var plural = string.Empty; if (errorCount > 1) { plural = "s"; } throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains class value with missing non-optional member" + plural + ": " + sb.ToString() + " " + parseStack.Dump()); }
object ParseStruct( XmlNode node, Type valueType, ParseStack parseStack) { if (valueType.IsPrimitive) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains class value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Nullable <>)) { valueType = valueType.GetGenericArguments()[0]; } object retObj; try { retObj = Activator.CreateInstance(valueType); } catch (Exception) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains class value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected (as type " + valueType.Name + ") " + parseStack.Dump()); } // Note: mapping action on a class is only applied locally - it // does not override the global mapping action when members of the // class are parsed MappingAction localAction = _config.MappingAction; if (valueType != null) { parseStack.Push("class mapped to type " + valueType.Name); localAction = AttributeHelper.StructMappingAction(valueType, localAction); } else { parseStack.Push("struct"); } // create map of field names and remove each name from it as // processed so we can determine which fields are missing // TODO: replace HashTable with lighter collection Hashtable names = new Hashtable(); foreach (FieldInfo fi in valueType.GetFields()) { if (Attribute.IsDefined(fi, typeof(NonSerializedAttribute))) { continue; } names.Add(fi.Name, fi.Name); } foreach (PropertyInfo pi in valueType.GetProperties()) { if (Attribute.IsDefined(pi, typeof(NonSerializedAttribute))) { continue; } names.Add(pi.Name, pi.Name); } XmlNode[] members = node.SelectChildNodes("member"); int fieldCount = 0; foreach (XmlNode member in members) { if (member.Name != "member") { continue; } var(nameNode, nameIsDuplicated) = member.SelectPossibleDoupletteNode("name"); var(valueNode, valueIsDuplicated) = member.SelectPossibleDoupletteNode("value"); if (nameNode == null || nameNode.FirstChild == null) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains a member with missing name" + " " + parseStack.Dump()); } if (nameIsDuplicated) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains member with more than one name element" + " " + parseStack.Dump()); } string name = nameNode.FirstChild.Value; if (valueNode == null) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains class member " + name + " with missing value " + " " + parseStack.Dump()); } if (valueIsDuplicated) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains member with more than one value element" + " " + parseStack.Dump()); } string structName = AttributeHelper.GetStructName(valueType, name); if (structName != null) { name = structName; } MemberInfo mi = valueType.GetField(name); if (mi == null) { mi = valueType.GetProperty(name); } if (mi == null) { continue; } if (names.Contains(name)) { names.Remove(name); } else { if (Attribute.IsDefined(mi, typeof(NonSerializedAttribute))) { parseStack.Push(String.Format("member {0}", name)); throw new XmlRpcNonSerializedMember("Cannot map XML-RPC class " + "member onto member marked as [NonSerialized]: " + " " + parseStack.Dump()); } if (!_config.IgnoreDuplicateMembers()) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains class value with duplicate member " + nameNode.FirstChild.Value + " " + parseStack.Dump()); } else { continue; // ignore duplicate member } } object valObj = null; switch (mi.MemberType) { case MemberTypes.Field: FieldInfo fi = (FieldInfo)mi; if (valueType == null) { parseStack.Push(String.Format("member {0}", name)); } else { parseStack.Push(String.Format("member {0} mapped to type {1}", name, fi.FieldType.Name)); } try { XmlNode vvvNode = valueNode.SelectValueNode(); valObj = ParseValue(vvvNode, fi.FieldType, parseStack); } catch (XmlRpcInvalidXmlRpcException) { if (valueType != null && localAction == MappingAction.Error) { MappingAction memberAction = AttributeHelper.MemberMappingAction(valueType, name, MappingAction.Error); if (memberAction == MappingAction.Error) { throw; } } } finally { parseStack.Pop(); } fi.SetValue(retObj, valObj); break; case MemberTypes.Property: PropertyInfo pi = (PropertyInfo)mi; if (valueType == null) { parseStack.Push(String.Format("member {0}", name)); } else { parseStack.Push(String.Format("member {0} mapped to type {1}", name, pi.PropertyType.Name)); } XmlNode vvNode = valueNode.SelectValueNode(); valObj = ParseValue(vvNode, pi.PropertyType, parseStack); parseStack.Pop(); pi.SetValue(retObj, valObj, null); break; } fieldCount++; } if (localAction == MappingAction.Error && names.Count > 0) { ReportMissingMembers(valueType, names, parseStack); } parseStack.Pop(); return(retObj); }
public object ParseHashtable(XmlNode node, ParseStack parseStack) { var retObj = new XmlRpcStruct(); parseStack.Push("class mapped to XmlRpcStruct"); try { var members = node.SelectChildNodes("member"); foreach (var member in members) { var(nameNode, hasMultipleNameNodes) = member.SelectPossibleDoupletteNode("name"); var(valueNode, hasMultipleValueNodes) = member.SelectPossibleDoupletteNode("value"); if (nameNode == null || nameNode.FirstChild == null) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains a member with missing name" + " " + parseStack.Dump()); } if (hasMultipleNameNodes) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains member with more than one name element" + " " + parseStack.Dump()); } var rpcName = nameNode.FirstChild.Value; if (valueNode == null) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains class member " + rpcName + " with missing value " + " " + parseStack.Dump()); } if (hasMultipleValueNodes) { throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains member with more than one value element" + " " + parseStack.Dump()); } if (retObj.Contains(rpcName)) { if (_config.IgnoreDuplicateMembers()) { continue; } throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType + " contains class value with duplicate member " + nameNode.FirstChild.Value + " " + parseStack.Dump()); } parseStack.Push($"member {rpcName}"); try { var vvNode = valueNode.SelectValueNode(); var valObj = ParseValue(vvNode, null, parseStack); retObj.Add(rpcName, valObj); } finally { parseStack.Pop(); } } } finally { parseStack.Pop(); } return(retObj); }
object ParseArray(XmlNode node, Type valueType, ParseStack parseStack) { // required type must be an array if (valueType != null && !(valueType.IsArray == true || valueType == typeof(Array) || valueType == typeof(object))) { throw new XmlRpcTypeMismatchException(parseStack.ParseType + " contains array value where " + XmlRpcServiceInfo.GetXmlRpcTypeString(valueType) + " expected " + parseStack.Dump()); } if (valueType != null) { XmlRpcType xmlRpcType = XmlRpcServiceInfo.GetXmlRpcType(valueType); if (xmlRpcType == XmlRpcType.MultiDimArray) { parseStack.Push("array mapped to type " + valueType.Name); object ret = ParseMultiDimArray(node, valueType, parseStack); return(ret); } parseStack.Push("array mapped to type " + valueType.Name); } else { parseStack.Push("array"); } var dataNode = node.SelectSingleNode("data"); var childNodes = dataNode.SelectChildNodes("value"); var nodeCount = childNodes.Length; var elements = new object[nodeCount]; // determine type of array elements Type elemType; if (valueType != null && valueType != typeof(Array) && valueType != typeof(object)) { elemType = valueType.GetElementType(); } else { elemType = typeof(object); } bool bGotType = false; Type useType = null; int i = 0; foreach (XmlNode vNode in childNodes) { parseStack.Push(String.Format("element {0}", i)); XmlNode vvNode = vNode.SelectValueNode(); elements[i++] = ParseValue(vvNode, elemType, parseStack, out Type parsedType, out Type parsedArrayType); if (bGotType == false) { useType = parsedArrayType; bGotType = true; } else { if (useType != parsedArrayType) { useType = null; } } parseStack.Pop(); } object[] args = new object[1]; args[0] = nodeCount; object retObj; if (valueType != null && valueType != typeof(Array) && valueType != typeof(object)) { retObj = Activator.CreateInstance(valueType, args); } else { if (useType == null) { retObj = Activator.CreateInstance(typeof(object[]), args); } else { retObj = Activator.CreateInstance(useType, args); } } for (int j = 0; j < elements.Length; j++) { ((Array)retObj).SetValue(elements[j], j); } parseStack.Pop(); return(retObj); }