/// <summary> /// Serialize the root object for a request. /// This should always be BroadsoftDocument /// </summary> /// <param name="document"></param> /// <returns></returns> private XElement SerializeRoot <T>(BroadsoftDocument <T> document) where T : OCICommand { var documentType = typeof(BroadsoftDocument <T>); var ns = XNamespace.None; // By default, element name will by the class name var elementName = "BroadsoftDocument"; // XmlRoot attribute will contain useful information such as namespace var xmlRootAttr = AttributeUtil.Get <XmlRootAttribute>(documentType); if (xmlRootAttr != null) { // Override defaults if attribute properties are set if (xmlRootAttr.Namespace != null) { ns = xmlRootAttr.Namespace; } if (!string.IsNullOrEmpty(xmlRootAttr.ElementName)) { elementName = xmlRootAttr.ElementName; } } // Element contents is an object contianing all attributes and elements under this element return(new XElement(ns + elementName, new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"), new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"), GetElementContentsForInstance(documentType, document))); }
/// <summary> /// Retrieves all the properties/groups that are part of the group /// </summary> /// <param name="options"></param> /// <param name="instanceType"></param> /// <returns></returns> private IEnumerable <string> OptionNames(IEnumerable <ChoiceOption> options, Type instanceType) { return(options.Select(opt => { if (opt is ChoiceFieldOption fieldOpt) { return fieldOpt.Name; } else if (opt is ChoiceSequenceOption sequenceOpt) { var seqProperties = instanceType.GetProperties() .Where(prop => AttributeUtil.Get <XmlIgnoreAttribute>(prop) == null) .Where(prop => { var groupAttribute = AttributeUtil.Get <GroupAttribute>(prop); return groupAttribute != null && groupAttribute.Id == sequenceOpt.Sequence.Id; }) .Select(prop => prop.Name); return $"[{string.Join(", ", seqProperties)}]"; } else { throw new InvalidOperationException("Unknown choice option"); } })); }
/// <summary> /// Determine if a field in sequence has been set /// </summary> /// <param name="sequence"></param> /// <param name="instance"></param> /// <returns></returns> private bool IsSequenceSet(Sequence sequence, object instance) { var set = false; // Get properties that are part of sequence var requiredProperties = instance.GetType().GetProperties() .Where(prop => AttributeUtil.Get <XmlIgnoreAttribute>(prop) == null) .Where(prop => { var groupAttribute = AttributeUtil.Get <GroupAttribute>(prop); return(groupAttribute != null && groupAttribute.Id == sequence.Id); }); foreach (var property in requiredProperties) { if (IsFieldSet(property, instance)) { set = true; } } return(set); }
/// <summary> /// Validate sequence group /// </summary> /// <param name="instance"></param> /// <returns>A list of all errors encountered.</returns> public override IEnumerable <ValidationError> Validate(object instance) { var errors = new List <ValidationError>(); var type = instance.GetType(); // Get all properties on object that are part of this group and required var requiredProperties = type.GetProperties() .Where(prop => AttributeUtil.Get <OptionalAttribute>(prop) == null) .Where(prop => AttributeUtil.Get <XmlIgnoreAttribute>(prop) == null) .Where(prop => { var groupAttribute = AttributeUtil.Get <GroupAttribute>(prop); return(groupAttribute != null && groupAttribute.Id == Id); }); // Check if required properties are set foreach (var prop in requiredProperties) { var set = false; if (Validator.IsPropertySpecified(prop, instance)) { var value = prop.GetValue(instance, null); var valueAsEnumerable = value as IEnumerable <object>; if (valueAsEnumerable != null) { // Enumerables must have at least one entry set = valueAsEnumerable.Count() > 0; } else { // Other values must not be null set = value != null; } } if (!set) { errors.Add(new FieldNotSetError(instance, prop.Name)); } } // Validate any child groups if (Children != null) { foreach (var child in Children) { errors.AddRange(child.Validate(instance)); } } return(errors); }
/// <summary> /// Serializes an Enum value to a string /// </summary> /// <param name="enumType"></param> /// <param name="value"></param> /// <returns></returns> private string EnumForValue(Type enumType, string value) { var xmlEnumAttr = AttributeUtil.Get <XmlEnumAttribute>(enumType.GetField(value)); if (xmlEnumAttr == null) { throw new ArgumentException("Enum doesn't have XmlEnum attribute", nameof(value)); } return(xmlEnumAttr.Name); }
public void TestAttributeUtilGet() { TestClass2 item = new TestClass2(7, "test", 0); object value = AttributeUtil.Get(() => item.Id, typeof(StringValueAttribute), "Value"); Assert.IsNotNull(value); Assert.IsTrue(((string)value).StartsWith("IdTest")); value = AttributeUtil.Get(() => item.Name, typeof(StringValueAttribute), "Value"); Assert.AreEqual("NameTest", value); value = AttributeUtil.Get(() => item.Age, typeof(StringValueAttribute), "Value"); Assert.IsNull(value); }
/// <summary> /// Determines if a value is an object that should be ran through the validator /// </summary> /// <param name="value"></param> /// <returns>If an object should be ran through the Validate method.</returns> private static bool IsValidatableObject(object value) { if (value is object) { var type = value.GetType(); return(!type.IsPrimitive && // Ignore primitives !type.Equals(typeof(string)) && // Ignore strings !type.IsEnum && // Ignore enums AttributeUtil.Get <XmlIgnoreAttribute>(type) == null); // Ignore anything explicitly marked as ignore } else { return(false); } }
/// <summary> /// Returns all the child attributes and elements in serialized form for an object /// </summary> /// <param name="type"></param> /// <param name="instance"></param> /// <returns></returns> private List <XObject> GetElementContentsForInstance(Type type, object instance) { var contents = new List <XObject>(); // Get all properties that are marked with XmlElementAttribute or XmlAttributeAttribute var properties = type.GetProperties() .Where(prop => AttributeUtil.GetAll(prop).Any(attr => attr is XmlElementAttribute || attr is XmlAttributeAttribute)); foreach (var property in properties) { if (IsPropertySpecified(property, type, instance)) { var elementAttr = AttributeUtil.Get <XmlElementAttribute>(property); var attributeAttr = AttributeUtil.Get <XmlAttributeAttribute>(property); // Handle property if it's an XML Element if (elementAttr != null) { var propertyValue = property.GetValue(instance); // If property value is a list, create an element for each entry. Else just create an element for the value. var isList = property.PropertyType.GetInterfaces() .Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IList <>)); if (isList) { foreach (var entry in (IEnumerable)propertyValue) { contents.Add(SerializeElement(property, entry, elementAttr)); } } else { contents.Add(SerializeElement(property, propertyValue, elementAttr)); } } // Handle property if it's an XML Attribute else if (attributeAttr != null) { contents.Add(SerializeAttribute(property, property.GetValue(instance), attributeAttr)); } } } return(contents); }
/// <summary> /// Determine if the given property was manually set on an object /// </summary> /// <param name="property"></param> /// <param name="instance"></param> public static bool IsPropertySpecified(PropertyInfo property, object instance) { // Skip properties marked as Ignored to serializer if (AttributeUtil.Get <XmlIgnoreAttribute>(property) != null) { return(false); } var type = instance.GetType(); var specifiedFieldName = $"{property.Name}Specified"; var specifiedProperty = type.GetProperty(specifiedFieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (specifiedProperty == null) { throw new InvalidOperationException($"{specifiedFieldName} does not exist"); } return((bool)specifiedProperty.GetValue(instance)); }
/// <summary> /// Deserialize a string to enum /// </summary> /// <param name="value"></param> /// <param name="targetType"></param> /// <returns></returns> private object DeserializeEnum(string value, Type targetType) { if (!targetType.IsEnum) { throw new ArgumentException("Type is not an Enum", nameof(targetType)); } var enumValue = targetType.GetFields().Where(field => { var enumAttr = AttributeUtil.Get <XmlEnumAttribute>(field); return(enumAttr?.Name == value); }).FirstOrDefault(); if (enumValue == null) { throw new InvalidOperationException($"Value {value} has no corresponding enum value on {targetType}"); } return(Enum.Parse(targetType, enumValue.Name)); }
/// <summary> /// Retrieve Sequence and Choice groups on an object /// </summary> /// <param name="instance"></param> /// <returns></returns> private static IEnumerable <Group> GetGroups(object instance) { var groups = new List <Group>(); var type = instance.GetType(); while (type != null) { // Object should have a Groups attribute containing the group details as JSON var groupsAttr = AttributeUtil.Get <GroupsAttribute>(type); if (groupsAttr == null) { throw new ArgumentException("No Groups attribute on object", nameof(instance)); } if (groupsAttr != null) { // Parse JSON using (var ms = new MemoryStream(Encoding.ASCII.GetBytes(groupsAttr.Json))) { groups.AddRange(_serializer.ReadObject(ms) as List <Group>); } } if ((type.BaseType != null) && (type.BaseType != typeof(Object))) { type = type.BaseType; } else { type = null; } } return(groups); }
/// <summary> /// Deserialize an element /// </summary> /// <param name="element"></param> /// <param name="targetType"></param> /// <returns></returns> private object DeserializeElement(XElement element, Type targetType) { // Serialize to primitive value if primitive or enum if (IsValueType(targetType)) { return(DeserializeValueType(element.Value, targetType)); } // If the target type is abstract, then there should be an attribute on the XML element // telling us the concrete type if (targetType.IsAbstract) { var typeAttribute = element.Attribute(xsiNamespace + "type"); if (typeAttribute != null) { targetType = Type.GetType($"{ModelNamespace}.{typeAttribute.Value.Replace("c:", "C.")}"); } } if (targetType == null) { throw new ArgumentException("targetType cannot be null", nameof(targetType)); } var obj = Activator.CreateInstance(targetType); // Iterate through properties in the object and set it var properties = targetType.GetProperties(); foreach (var property in properties) { var propertyType = property.PropertyType; var propertyIsList = propertyType.GetTypeInfo().IsGenericType&& propertyType.GetGenericTypeDefinition() == typeof(List <>); var elementAttr = AttributeUtil.Get <XmlElementAttribute>(property); var attributeAttr = AttributeUtil.Get <XmlAttributeAttribute>(property); // Handle when property is for an XML element if (elementAttr != null) { if (propertyIsList) { // Since the destination is a list, we need to get the type the list is composed of so we can construct them. var individualType = propertyType.GetTypeInfo().GenericTypeArguments[0]; // Get elements from the XML for the property var childElements = element.Elements(elementAttr.ElementName); if (childElements.Count() > 0) { var list = Activator.CreateInstance(propertyType) as IList; foreach (var childElement in childElements) { list.Add(DeserializeElement(childElement, individualType)); } property.SetValue(obj, list); } } else { var childElement = element.Element(elementAttr.ElementName); if (childElement != null) { property.SetValue(obj, DeserializeElement(childElement, propertyType)); } } } // Handle when property is for an XML attribute else if (attributeAttr != null) { var attribute = element.Attribute(attributeAttr.AttributeName); if (attribute != null) { property.SetValue(obj, attribute.Value); } } } return(obj); }
/// <summary> /// Validate choice group /// </summary> /// <param name="instance"></param> /// <returns>A list of all errors encountered.</returns> public override IEnumerable <ValidationError> Validate(object instance) { var errors = new List <ValidationError>(); var type = instance.GetType(); // Get all properties on object that are part of this group var requiredProperties = type.GetProperties() .Where(prop => AttributeUtil.Get <XmlIgnoreAttribute>(prop) == null) .Where(prop => { var groupAttribute = AttributeUtil.Get <GroupAttribute>(prop); return(groupAttribute != null && groupAttribute.Id == Id); }); var options = new List <ChoiceOption>(); options.AddRange(requiredProperties.Select(prop => new ChoiceFieldOption() { Name = prop.Name, Optional = AttributeUtil.Get <OptionalAttribute>(prop) != null, Set = IsFieldSet(prop, instance) })); if (Children != null) { // Find any Sequences that are children foreach (var child in Children) { if (child is Sequence sequence) { var isSet = IsSequenceSet(sequence, instance); // If any of the sequences have set fields, validate the Sequence to // enforce all required fields if (isSet) { errors.AddRange(sequence.Validate(instance)); } options.Add(new ChoiceSequenceOption() { Sequence = sequence, Optional = false, Set = isSet }); } } } var setMembers = options.Where(opt => opt.Set); var nonOptionalMembers = options.Where(opt => !opt.Optional); if ((setMembers.Count() == 0) && (nonOptionalMembers.Count() != 0) && !Optional) { errors.Add(new ChoiceNotSetError(instance, OptionNames(options, type))); } else if (setMembers.Count() > 1) { errors.Add(new InvalidChoiceError(instance, OptionNames(options, type))); } return(errors); }