Object ParseStruct(
XmlNode node,
Type valueType,
ParseStack parseStack,
MappingAction mappingAction)
{
if (valueType.IsPrimitive || valueType.IsArray)
{
throw new XmlRpcTypeMismatchException(parseStack.ParseType
+ " contains struct value where "
+ XmlRpcServiceInfo.GetXmlRpcTypeString(valueType)
+ " expected (as type " + valueType.Name + ") "
+ StackDump(parseStack));
}
#if !FX1_0
if (valueType.IsGenericType
&& valueType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
valueType = valueType.GetGenericArguments()[0];
}
#endif
object retObj;
try
{
retObj = Activator.CreateInstance(valueType);
}
catch (Exception ex)
{
throw new XmlRpcTypeMismatchException(parseStack.ParseType
+ " contains struct value where "
+ XmlRpcServiceInfo.GetXmlRpcTypeString(valueType)
+ " expected (unable to create instance of " + valueType.Name
+ ", possibly no default constructor) "
+ StackDump(parseStack));
}
// Note: mapping action on a struct is only applied locally - it
// does not override the global mapping action when members of the
// struct are parsed
MappingAction localAction = mappingAction;
if (valueType != null)
{
parseStack.Push("struct mapped to type " + valueType.Name);
localAction = StructMappingAction(valueType, mappingAction);
}
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())
{
names.Add(fi.Name, fi.Name);
}
foreach (PropertyInfo pi in valueType.GetProperties())
{
names.Add(pi.Name, pi.Name);
}
XmlNodeList members = node.ChildNodes;
int fieldCount = 0;
foreach (XmlNode member in members)
{
if (member.Name != "member")
continue;
XmlNode nameNode;
bool dupName;
XmlNode valueNode;
bool dupValue;
SelectTwoNodes(member, "name", out nameNode, out dupName, "value",
out valueNode, out dupValue);
if (nameNode == null)
throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
+ " contains a member with missing name element"
+ " " + StackDump(parseStack));
if (dupName)
throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
+ " contains member with more than one name element"
+ " " + StackDump(parseStack));
string name = nameNode.FirstChild.Value;
if (valueNode == null)
throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
+ " contains struct member " + name + " with missing value "
+ " " + StackDump(parseStack));
if (dupValue)
throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
+ " contains member with more than one value element"
+ " " + StackDump(parseStack));
string structName = GetStructName(valueType, name);
if (structName != null)
name = structName;
if (names.Contains(name))
names.Remove(name);
else
{
if (!IgnoreDuplicateMembers
&& (valueType.GetField(name) != null || valueType.GetProperty(name) != null))
throw new XmlRpcInvalidXmlRpcException(parseStack.ParseType
+ " contains struct value with duplicate member "
+ nameNode.FirstChild.Value
+ " " + StackDump(parseStack));
else
continue; // ignore duplicate member
}
MemberInfo[] mis = valueType.GetMember(name);
if (mis.Length == 0)
{
continue; // allow unexpected members
}
Object valObj = null;
switch (mis[0].MemberType)
{
case MemberTypes.Field:
FieldInfo fi = (FieldInfo)mis[0];
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
{
valObj = ParseValue(valueNode.FirstChild, fi.FieldType,
parseStack, mappingAction);
}
catch(XmlRpcInvalidXmlRpcException)
{
if (valueType != null && localAction == MappingAction.Error)
{
MappingAction memberAction = MemberMappingAction(valueType,
name, MappingAction.Error);
if (memberAction == MappingAction.Error)
throw;
}
}
finally
{
parseStack.Pop();
}
fi.SetValue(retObj, valObj);
break ;
case MemberTypes.Property :
PropertyInfo pi = (PropertyInfo)mis[0] ;
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));
valObj = ParseValue(valueNode.FirstChild, pi.PropertyType,
parseStack, mappingAction);
parseStack.Pop();
pi.SetValue(retObj, valObj, null);
break ;
}
fieldCount++;
}
if (localAction == MappingAction.Error && names.Count > 0)
ReportMissingMembers(valueType, names, parseStack);
parseStack.Pop();
return retObj;
}