XPathQueryGenerator.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime.Serialization
  5. {
  6. using System;
  7. using System.Text;
  8. using System.Reflection;
  9. using System.Globalization;
  10. using System.Collections.Generic;
  11. using System.Xml;
  12. public static class XPathQueryGenerator
  13. {
  14. const string XPathSeparator = "/";
  15. const string NsSeparator = ":";
  16. public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, out XmlNamespaceManager namespaces)
  17. {
  18. return CreateFromDataContractSerializer(type, pathToMember, null, out namespaces);
  19. }
  20. // Here you can provide your own root element Xpath which will replace the Xpath of the top level element
  21. public static string CreateFromDataContractSerializer(Type type, MemberInfo[] pathToMember, StringBuilder rootElementXpath, out XmlNamespaceManager namespaces)
  22. {
  23. if (type == null)
  24. {
  25. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("type"));
  26. }
  27. if (pathToMember == null)
  28. {
  29. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("pathToMember"));
  30. }
  31. DataContract currentContract = DataContract.GetDataContract(type);
  32. ExportContext context;
  33. if (rootElementXpath == null)
  34. {
  35. context = new ExportContext(currentContract);
  36. }
  37. else
  38. {
  39. // use the provided xpath for top level element
  40. context = new ExportContext(rootElementXpath);
  41. }
  42. for (int pathToMemberIndex = 0; pathToMemberIndex < pathToMember.Length; pathToMemberIndex++)
  43. {
  44. currentContract = ProcessDataContract(currentContract, context, pathToMember[pathToMemberIndex]);
  45. }
  46. namespaces = context.Namespaces;
  47. return context.XPath;
  48. }
  49. static DataContract ProcessDataContract(DataContract contract, ExportContext context, MemberInfo memberNode)
  50. {
  51. if (contract is ClassDataContract)
  52. {
  53. return ProcessClassDataContract((ClassDataContract)contract, context, memberNode);
  54. }
  55. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.QueryGeneratorPathToMemberNotFound)));
  56. }
  57. static DataContract ProcessClassDataContract(ClassDataContract contract, ExportContext context, MemberInfo memberNode)
  58. {
  59. string prefix = context.SetNamespace(contract.Namespace.Value);
  60. foreach (DataMember member in GetDataMembers(contract))
  61. {
  62. if (member.MemberInfo.Name == memberNode.Name && member.MemberInfo.DeclaringType.IsAssignableFrom(memberNode.DeclaringType))
  63. {
  64. context.WriteChildToContext(member, prefix);
  65. return member.MemberTypeContract;
  66. }
  67. }
  68. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.QueryGeneratorPathToMemberNotFound)));
  69. }
  70. static IEnumerable<DataMember> GetDataMembers(ClassDataContract contract)
  71. {
  72. if (contract.BaseContract != null)
  73. {
  74. foreach (DataMember baseClassMember in GetDataMembers(contract.BaseContract))
  75. {
  76. yield return baseClassMember;
  77. }
  78. }
  79. if (contract.Members != null)
  80. {
  81. foreach (DataMember member in contract.Members)
  82. {
  83. yield return member;
  84. }
  85. }
  86. }
  87. class ExportContext
  88. {
  89. XmlNamespaceManager namespaces;
  90. int nextPrefix;
  91. StringBuilder xPathBuilder;
  92. public ExportContext(DataContract rootContract)
  93. {
  94. this.namespaces = new XmlNamespaceManager(new NameTable());
  95. string prefix = SetNamespace(rootContract.TopLevelElementNamespace.Value);
  96. this.xPathBuilder = new StringBuilder(XPathQueryGenerator.XPathSeparator + prefix + XPathQueryGenerator.NsSeparator + rootContract.TopLevelElementName.Value);
  97. }
  98. public ExportContext(StringBuilder rootContractXPath)
  99. {
  100. this.namespaces = new XmlNamespaceManager(new NameTable());
  101. this.xPathBuilder = rootContractXPath;
  102. }
  103. public void WriteChildToContext(DataMember contextMember, string prefix)
  104. {
  105. this.xPathBuilder.Append(XPathQueryGenerator.XPathSeparator + prefix + XPathQueryGenerator.NsSeparator + contextMember.Name);
  106. }
  107. public XmlNamespaceManager Namespaces
  108. {
  109. get
  110. {
  111. return this.namespaces;
  112. }
  113. }
  114. public string XPath
  115. {
  116. get
  117. {
  118. return this.xPathBuilder.ToString();
  119. }
  120. }
  121. public string SetNamespace(string ns)
  122. {
  123. string prefix = namespaces.LookupPrefix(ns);
  124. if (prefix == null || prefix.Length == 0)
  125. {
  126. prefix = "xg" + (this.nextPrefix++).ToString(NumberFormatInfo.InvariantInfo);
  127. Namespaces.AddNamespace(prefix, ns);
  128. }
  129. return prefix;
  130. }
  131. }
  132. }
  133. }