Programming Enhanced Presence Schemas: Parsing Category Instances (Part 3 of 3)

Summary:   In this article you learn how to parse a schematized enhanced presence category instance with the Microsoft.NET Framework System.Xml.Serialization and System.Reflection namespaces. This kind of parsing makes your parsing code reusable and subject to fewer errors than direct manipulations of the raw XML strings that use either XPath with regular expression or XML Document Object Model (DOM).

This article is the final installment of a three-part series of articles. For information about part 1 and part 2, see Additional Resources.

Applies to:   Microsoft Lync 2010 | Microsoft Office Communicator 2007 | Microsoft Office Communicator 2007 R2

Published:   February 2011 | Provided by:   Kurt De Ding, Microsoft | About the Author

Contents

  • Introduction

  • Creating the Visual Studio Project

  • Converting a Category Instance XML String to the Schematized CLR Object

  • Parsing the Schematized Presence Category Instance

  • Testing the Application

  • Conclusion

  • Additional Resources

Introduction

When subscribing or querying an enhanced presence category, you may need to parse the received category instances, especially when the category instance data is not encapsulated by the unified communications API that you used to build the application.

Subscribing to enhanced presence categories involves parsing XML strings of the category instance data that is received over the wire. Reading XML tags using XPath with regular expressions can be tedious and introduce errors. If the System.Xml namespace and the XML Document Object Model (DOM) are used to extract the XML data, the extraction process can be complex if the data structure is not known.

With the enhanced presence category instance, the XML data is prescribed by a given XML schema and XML strings can be deserialized into instances of Microsoft .NET Framework types. In part 1 of this series, you learned how to serialize enhanced presence category instance objects to obtain the corresponding XML strings and to deserialize the category instance XML strings to obtain the corresponding schema objects. By using the deserialized schema objects, parsing the XML data becomes a fairly simple task of reading properties off the objects.

If you use strongly typed .NET Framework objects, an application can apply the .NET Framework System.Reflection namespace on the schematized objects to discover the specific enhanced presence data details. This approach makes it possible to generalize the implementation to parse the enhanced presence data beyond the currently defined schemas, which makes your code more reusable.

In this article, the final installment of a three-part series of articles, you learn how to parse enhanced presence category instances, received in a presence subscription, by enumerating and reflecting the properties of a category instance. The process uses the Microsoft.Rtc.Sdk.Categories.dll assembly (generated in part 1 of this series) and the System.Xml.Serialization and System.Reflection namespaces to complete two tasks:

  1. Convert a category instance XML string to a schematized CLR object.

  2. Parse the schematized category instance.

To convert a category instance XML string to a schematized CLR object

  • Deserialize an XML string into an instance of a CLR class of the Microsoft.Rtc.Sdk.Categories.dll assembly.

    The XML string is received in a presence subscription. The CLR class corresponds to an XML schema definition (XSD) type.

To parse the schematized category instance

  • Enumerate the properties of the category instance and then reflect on each property to determine the property specifics. These include the property type, name, and value.

    The results can then be presented to a UI serving as a general-purpose object viewer or editor for any schematized enhanced presence category instance.

In a Microsoft Lync Server 2010 or Microsoft Office Communications Server deployment, Microsoft Lync 2010 API or Microsoft Office Communicator Automation API exposes the enhanced presence category data only as properties of objects of the API. The API is responsible for parsing the raw XML strings. In this case, the technique discussed in this article does not apply. In Microsoft Unified Communications Managed API (UCMA), frequently used enhanced presence category data is encapsulated by UCMA types. However, the raw XML strings are still available for an application. This technique can still be useful in a UCMA application. It is especially valuable if the application deals with schematized custom presence category instances.

In the following sections, we discuss how to complete these tasks in a C# application.

Creating the Visual Studio Project

To create the Visual Studio project

  1. Start Microsoft Visual Studio 2008 development system, click File and then click New Project. The New Project dialog box appears.

  2. In the New Project dialog box, select Visual C# as the project type and Console Application as the template.

  3. In the Name text box, type PresenceParser for the project name, and then click OK. The PresenceParser - Microsoft Visual Studio window appears.

  4. In the Solution Explorer - PresenceParser pane, right-click References and then select Add Reference.

  5. Click the Browse tab in the Add Reference dialog box, select Microsoft.Rtc.Sdk.Categories.dll (the enhanced presence schemas assembly that are created in part 1 of this series), and then click OK.

To parse an enhanced presence category instance, the PresenceParser project contains an EpParser class that has the following methods.

  • The CreateCategoryInstanceObject method creates a CLR object from an input XML string of a given category instance using the System.Xml.Serialization namespace.

  • The ParseCategoryInstance method enumerates the properties of a category instance object by using the System.Reflection namespace.

  • The ParseCategoryInstanceProperty method parses the value of a property using the System.Reflection namespace.

Converting a Category Instance XML String to the Schematized CLR Object

The following steps and code show how to create the CLR object of a category name, given a raw XML string of a category instance. The operations involve matching the category name to a schematized category type supported in the Microsoft.Rtc.Sdk.Categories assembly and then deserializing the input XML string into a CLR object of the schematized category type. The detailed implementation appears in the CreateCategoryInstanceObject and DeserializeXml methods of the EpParser class.

To convert a category instance XML string to the schematized CLR object

  1. Open your PresenceParser project in Visual Studio.

  2. Add a C# class file to this project, and then name this file EpParser.cs.

  3. In EpParser.cs, add the following code statements to the EpParser class definition.

    namespace PresenceParser
    {
        public class EpParser
        {
            public object CreateCategoryInstanceObject(string categoryInstanceXml, string categoryName)
            {
                object obj = null;
                Type schemaType = null;
                // Matching the category name with the category instance type 
               // to which the given category instance XML string is to be deserialized.
                if (categoryName == "state")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.State.stateType);
                else if (categoryName == "note")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.Note.noteType);
                else if (categoryName == "calendarData")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.CalendarData.calendarType);
                else if (categoryName == "contactCard")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.ContactCard.contactCardType);
                else if (categoryName == "alerts")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.AlertsOptions.alertsType);
                else if (categoryName == "device")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.Device.deviceType);
                else if (categoryName == "otherOptions")
                    type = typeof(Microsoft.Rtc.Sdk.Categories.OtherOptions.otherOptionsType);
                else if (categoryName == "rccOptions")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.RccOptions.rccOptionsType);
                else if (categoryName == "routing")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.Routing.routingtype);
                else if (categoryName == "services")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.Services.servicesType);
                else if (categoryName == "userInformation")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.UserOptions.userInformationType);
                else if (categoryName == "userProperties")
                    schemaType = typeof(Microsoft.Rtc.Sdk.Categories.UserProperties.userPropertiesType);
                else
                    return null;
    
                obj = DeserializeXml(categoryInstanceXml, schemaType);
                return obj;
            }
    
           object DeserializeXml(string xml, Type deserializedType)
           {
                if (xml == null || xml == string.Empty || deserializedType == null)
                    return null;
    
                StringReader sr = new StringReader(xml);
                XmlReaderSettings settings = new XmlReaderSettings();
                XmlReader xmlReader = XmlReader.Create(sr, settings);
    
                XmlSerializer serializer = new XmlSerializer(deserializedType);
                object obj = serializer.Deserialize(xmlReader);
                if (obj is Microsoft.Rtc.Sdk.Categories.State.stateType)
                {
                    // Spurious xsi:type attribute is removed from an anyAttr property.
                    RemoveSpuriousXsiTypeAttributeFromAnyAttrProperty(obj);
                }
                return obj;
    
           }
    
            void RemoveSpuriousXsiTypeAttributeFromAnyAttrProperty(object obj)
            {
                if (obj is Microsoft.Rtc.Sdk.Categories.State.stateType)
                {
                    // Remove spurious anyAttr property generated for the 
                    // xsi:type attribute, which is not explicitly defined by the Lync-specific 
                    // state schemas, but is nontheless used for the subtypes of the stateType, 
                    // including aggregateStateType, userState, calendarState, and machineState.
                    //
                    XmlAttribute[] anyAttrs = ((Microsoft.Rtc.Sdk.Categories.State.stateType)obj).AnyAttr;
                    if (anyAttrs != null)
                    {
                        foreach (XmlAttribute attr in anyAttrs)
                        {
                            if (attr.Name.ToLower() == "xsi:type")
                            {
                                int index = Array.IndexOf(anyAttrs, attr);
                                Array array = Array.CreateInstance(attr.GetType(), anyAttrs.Length - 1);
                                Array.Copy(anyAttrs, 0, array, 0, index);
                                Array.Copy(anyAttrs, index + 1, array, index, array.Length - index);
                                if (array.Length > 0)
                                    ((Microsoft.Rtc.Sdk.Categories.State.stateType)obj).AnyAttr =
                                        array as XmlAttribute[];
                                else
                                    ((Microsoft.Rtc.Sdk.Categories.State.stateType)obj).AnyAttr = null;
                            }
                        }
                    }
                }
            }
        }
    }
    

Parsing the Schematized Presence Category Instance

The following procedures show how to parse a schematized presence category instance in C# and then test an EpParser class application.

To parse schematized presence category instances

  1. Add the following field to the EpParser class definition appearing in EpParser.cs.

            const string INDENT = "   ";
    

    This field is used to render the display of the parsed result string.

  2. Add the following statements to the EpParser class definition. The ParseCategoryInstance method enumerates all properties of a given category instance through type reflection, and then calls the ParsePropertyInfo method to parse each resulting property.

            public string ParseCategoryInstance(object categoryInstance)
            {
                if (categoryInstance == null)
                    return null;
    
                Type categoryInstanceType = categoryInstance.GetType();
                if (!categoryInstanceType.Namespace.Contains("Microsoft.Rtc.Sdk.Categories"))
                {
                    // Ignore the object that is not in the enhanced presence category assembly.
                    return null;
                }
    
                string output = ""; // categoryInstanceType.Name + ":" + Environment.NewLine;
                PropertyInfo[] props = categoryInstanceType.GetProperties();
                if (props == null)
                {
                    output += INDENT + "The category instance contains no properties." + Environment.NewLine;
                    return output;
                }
    
                foreach (PropertyInfo p in props)
                {
                    output += ParsePropertyInfo(p, categoryInstance, INDENT) + Environment.NewLine;
                }
                return output;
            }
    
  3. Update the EpParser class definition with the following statements.

            string ParsePropertyInfo(PropertyInfo p, object owner, string indent)
            {
                string output = null;
                object pValueObj;
                string pValueStr = null;
                Type pType = p.PropertyType;
                string pName = p.Name;
    
                ParameterInfo[] indexParams = p.GetIndexParameters();
    
                if (indexParams != null && indexParams.Length > 0)
                {
                    Console.WriteLine("PARSING indexed property: " + pType.Name);
                    // Omit it.
                    return output;
                }
                else
                {
                    // Parse non-indexed properties.
                    pValueObj = p.GetValue(owner, null);
                    if (!IsPropertyNotSpecified(p, owner))
                        pValueStr = ParsePropertyValue(pValueObj, indent);
                    else
                        pValueStr = "unspecified";
                    output = /*Environment.NewLine + */ indent + pType.Name + " " + pName +
                        " = {" + pValueStr + "}";// +Environment.NewLine;
                    return output;
                }
            }
    
            bool IsPropertyNotSpecified(PropertyInfo p, object owner)
            {
                PropertyInfo propSpecified = owner.GetType().GetProperty(p.Name + "Specified");
                if (propSpecified != null && (propSpecified.PropertyType == typeof(bool)))
                {
                    return !((bool)propSpecified.GetValue(owner, null));
                }
                return false;
            }
    
  4. Update the EpParser class definition with the following statements.

            string ParsePropertyValue(object pValue, string indent)
            {
                if (pValue == null)
                    return null;
    
                string pValueString = null;
                Type pValueType = pValue.GetType();
    
                if (pValueType.IsPrimitive || pValueType == typeof(string) ||
                    pValueType.IsEnum || pValueType.IsValueType)
                {
                    pValueString = pValue.ToString();
                    return pValueString;
                }
    
                if (pValueType == typeof(XmlElement))
                {
                    XmlElement xmlElem = pValue as XmlElement;
                    pValueString = xmlElem.InnerXml;
                    return pValueString;
                }
    
                if (pValueType == typeof(XmlAttribute))
                {
                    XmlAttribute xmlAttr = pValue as XmlAttribute;
                    pValueString = xmlAttr.Name + " = " + xmlAttr.Value;
                    return pValueString;
                }
    
                if (pValue is Array)
                {
                    Array arr = pValue as Array;
                    pValueString = ""; 
                    for (int i = 0; i < arr.Length; i++)
                    {
                        object element = arr.GetValue(i);
                        pValueString += Environment.NewLine + indent +INDENT + 
                            pValueType.GetElementType().Name + "[" + i + "] = {" + Environment.NewLine +
                            ParsePropertyValue(element, indent + INDENT) + Environment.NewLine + indent + "}"; 
                    }
                    return pValueString;
                }
    
                if (pValueType != typeof(object))
                {
                    PropertyInfo[] props = pValueType.GetProperties();
                    if (props == null)
                        return null;
    
                    object propsOwner = pValue;
                    pValueString = null;
                    foreach (PropertyInfo p in props)
                    {
                        pValueString += indent + ParsePropertyInfo(p, propsOwner, indent) +Environment.NewLine;
                    }
                    return pValueString;
                }
    
                return pValueString;
            }
    

    The EpParser class is now fully implemented. The next step is to test how it works with a test application that parses two XML strings of a note and services category instance.

Testing the Application

To test the application

  1. In Solution Explorer, double-click the Program.cs file. The code window appears.

  2. Insert the following code statements at the top of the Program class definition, before the Main method. The noteXml string represents a note category instance received in a presence subscription. The servicesXml string represents a services category instance. They will be used to test the EpParser class.

            static string noteXml =
                "<note xmlns=\"https://schemas.microsoft.com/2006/09/sip/note\" " +
                "      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                "      xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"> " +
                "   <body type=\"personal\" uri=\"\">Hello, again, again</body> " +
                "</note>";
    
    
            static string servicesXml =
            "<services xmlns=\"https://schemas.microsoft.com/2006/09/sip/service\">" +
                "<service uri=\"sip:john@contoso.com\">" +
                    "<capabilities>" +
                        "<text render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<gifInk render=\"true\" capture=\"false\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<isfInk render=\"false\" capture=\"false\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<applicationSharing render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<confInvite render=\"true\" capture=\"false\" publish=\"true\" preferredEndpointId=\"a47340e3-4723-5558-b5d2-78b850aa2364\" deviceAvailability=\"3500\" />" +
                        "<voice render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<video render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<containerIntegrity render=\"false\" capture=\"false\" publish=\"true\" version=\"30\" preferredEndpointId=\"a47340e3-4723-5558-b5d2-78b850aa2364\" deviceAvailability=\"3500\" />" +
                        "<ucs render=\"false\" capture=\"false\" publish=\"true\" preferredEndpointId=\"a47340e3-4723-5558-b5d2-78b850aa2364\" deviceAvailability=\"3500\" />" +
                        "<breakthrough render=\"false\" capture=\"false\" publish=\"true\" preferredEndpointId=\"a47340e3-4723-5558-b5d2-78b850aa2364\" deviceAvailability=\"3500\" />" +
                        "<contentWhiteboard render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<contentPowerPoint render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                        "<contentNativeFile render=\"true\" capture=\"true\" publish=\"false\" preferredEndpointId=\"c5555f3c-d0a1-5d72-9169-b7299eaef96a\" deviceAvailability=\"3500\" />" +
                    "</capabilities>" +
                "</service>" +
                "<service uri=\"john@exchange.contoso.com\">" +
                    "<capabilities>" +
                        "<calendar render=\"false\" capture=\"false\" publish=\"true\" version=\"655616\" preferredEndpointId=\"a47340e3-4723-5558-b5d2-78b850aa2364\" deviceAvailability=\"3500\" />" +
                    "</capabilities>" +
                "</service>" +
            "</services>";
    
  3. Replace the default Main method definition, created by the Console Application template, with the following statements.

            static void Main(string[] args)
            {
                EpParser parser = new EpParser();
    
                object objNote = parser.CreateCategoryInstanceObject(noteXml, "note");
                string output = parser.ParseCategoryInstance(objNote);
                Console.WriteLine(objNote.GetType().Name + " note = {");
                Console.WriteLine(output);
                Console.WriteLine("}");
                Pause();
    
                object objServices = parser.CreateCategoryInstanceObject(servicesXml, "services");
                output = parser.ParseCategoryInstance(objServices);
                Console.WriteLine(objServices.GetType().Name + " services = {");
                Console.WriteLine(output);
                Console.WriteLine("}");
                Pause();
    
            }
    
            static void Pause()
            {
                Console.Write("Hit any key to continue");
                Console.ReadKey();
            }
    
  4. In Visual Studio, press F5 to build and run the project. When the code runs successfully, the result of parsing the CLR object of the note category instance appears in the following format.

    noteType note = {
       noteTypeBody[] body = {
          noteTypeBody[0] = {
             String type = {personal}
             String LCID = {}
             String uri = {}
             DateTime startTime = {unspecified}
             Boolean startTimeSpecified = {False}
             DateTime endTime = {unspecified}
             Boolean endTimeSpecified = {False}
             XmlAttribute[] AnyAttr = {}
             String Value = {Hello, again, again}
          }
       }
       delimiter[] delimiter = {}
       XmlElement[] Any = {}
       end end = {}
       extensionType extension = {}
       UInt32 majorVersion = {unspecified}
       Boolean majorVersionSpecified = {False}
       UInt32 minorVersion = {unspecified}
       Boolean minorVersionSpecified = {False}
       XmlAttribute[] AnyAttr = {}
    
    }
    

    The result of parsing the CLR object of the services category instance:

    servicesType services = {
       serviceType[] service = {
          serviceType[0] = {
             capabilitiesType capabilities = {
                capabilityType[] Items = {
                   capabilityType[0] = {
                      String uri = {}
                      Boolean render = {True}
                      Boolean capture = {True}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
                   }
                   capabilityType[1] = {
                      String uri = {}
                      Boolean render = {True}
                      Boolean capture = {False}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[2] = {
                      String uri = {}
                      Boolean render = {False}
                      Boolean capture = {False}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[3] = {
                      String uri = {}
                      Boolean render = {True}
                      Boolean capture = {True}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[4] = {
                      String uri = {}
                      Boolean render = {True}
                      Boolean capture = {True}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[5] = {
                      String uri = {}
                      Boolean render = {True}
                      Boolean capture = {True}
                      Boolean publish = {False}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {c5555f3c-d0a1-5d72-9169-b7299eaef96a}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[6] = {
                      String uri = {}
                      Boolean render = {False}
                      Boolean capture = {False}
                      Boolean publish = {True}
                      UInt32 version = {30}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {a47340e3-4723-5558-b5d2-78b850aa2364}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[7] = {
                      String uri = {}
                      Boolean render = {False}
                      Boolean capture = {False}
                      Boolean publish = {True}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {a47340e3-4723-5558-b5d2-78b850aa2364}
                      Boolean preferred = {False}
    
                   }
                   capabilityType[8] = {
                      String uri = {}
                      Boolean render = {False}
                      Boolean capture = {False}
                      Boolean publish = {True}
                      UInt32 version = {0}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {a47340e3-4723-5558-b5d2-78b850aa2364}
                      Boolean preferred = {False}
    
                   }
                }
                ItemsChoiceType[] ItemsElementName = {
                   ItemsChoiceType[0] = {text}
                   ItemsChoiceType[1] = {gifInk}
                   ItemsChoiceType[2] = {isfInk}
                   ItemsChoiceType[3] = {applicationSharing}
                   ItemsChoiceType[4] = {voice}
                   ItemsChoiceType[5] = {video}
                   ItemsChoiceType[6] = {containerIntegrity}
                   ItemsChoiceType[7] = {ucs}
                   ItemsChoiceType[8] = {breakthrough}
                }
                delimiter[] delimiter = {}
                XmlElement[] Any = {
                   XmlElement[0] = {      }
                   XmlElement[1] = {      }
                   XmlElement[2] = {      }
                   XmlElement[3] = {      }
                }
                end end = {}
                extensionType extension = {}
                String uri = {}
                Boolean preferred = {unspecified}
                Boolean preferredSpecified = {False}
                XmlAttribute[] AnyAttr = {}
             }
             delimiter[] delimiter = {}
             XmlElement[] Any = {}
             end end = {}
             extensionType extension = {}
             String uri = {sip:kdeding@microsoft.com}
             XmlAttribute[] AnyAttr = {}
    
          }
          serviceType[1] = {
             capabilitiesType capabilities = {
                capabilityType[] Items = {
                   capabilityType[0] = {
                      String uri = {}
                      Boolean render = {False}
                      Boolean capture = {False}
                      Boolean publish = {True}
                      UInt32 version = {655616}
                      UInt32 deviceAvailability = {3500}
                      Boolean deviceAvailabilitySpecified = {True}
                      XmlAttribute[] AnyAttr = {}
                      String preferredEndpointId = {a47340e3-4723-5558-b5d2-78b850aa2364}
                      Boolean preferred = {False}
    
                   }
                }
                ItemsChoiceType[] ItemsElementName = {
                   ItemsChoiceType[0] = {calendar}
                }
                delimiter[] delimiter = {}
                XmlElement[] Any = {}
                end end = {}
                extensionType extension = {}
                String uri = {}
                Boolean preferred = {unspecified}
                Boolean preferredSpecified = {False}
                XmlAttribute[] AnyAttr = {}
             }
             delimiter[] delimiter = {}
             XmlElement[] Any = {}
             end end = {}
             extensionType extension = {}
             String uri = {kdeding@exchange.microsoft.com}
             XmlAttribute[] AnyAttr = {}
    
          }
       }
       delimiter[] delimiter = {}
       XmlElement[] Any = {}
       end end = {}
       extensionType extension = {}
       UInt32 majorVersion = {unspecified}
       Boolean majorVersionSpecified = {False}
       UInt32 minorVersion = {unspecified}
       Boolean minorVersionSpecified = {False}
       XmlAttribute[] AnyAttr = {}
    }
    

Conclusion

In this article, you learned how to use Microsoft.Rtc.Sdk.Categories.dll and the .NET Framework System.Xml.Serialization and System.Reflection namespaces to complete the following tasks.

  • Convert an enhanced presence category instance XML string to a corresponding schematized CLR object by deserializing the XML string.

  • Parse the enhanced presence category instance by enumerating the properties of the CLR object and reflect on the resultant property information items to show the property detail. These include the property type, name, and value. When a property value is a non-trivial class type, the property value is parsed recursively.

Additional Resources

For more information, see the following resources:

About the Author

Kurt De Ding is a Senior Programming Writer in the Microsoft Office Content Publishing (UA) group.