/// <summary> /// Find the item in SpecificationData whose properties best match the given .NET type. /// </summary> private RuntimeTypeData FindBestMatchingSpecification(Type netType) { RuntimeTypeData bestMatch = null; int bestMatchingPropertiesCount = 0; Dictionary <string, System.Reflection.PropertyInfo> netProperties = new Dictionary <string, System.Reflection.PropertyInfo>(); foreach (System.Reflection.PropertyInfo pi in netType.GetProperties()) { netProperties[pi.Name.ToLowerInvariant()] = pi; } foreach (RuntimeTypeData candidate in this.SpecificationData) { int matchingProperties = 0; foreach (string propertyName in candidate.Properties.Keys) { if (netProperties.ContainsKey(propertyName) && (candidate.Properties[propertyName].Type == null || candidate.Properties[propertyName].Type.Type == null || candidate.Properties[propertyName].Type.Type.Equals(netProperties[propertyName].PropertyType))) { matchingProperties++; } } if (bestMatch == null || bestMatchingPropertiesCount < matchingProperties) { bestMatchingPropertiesCount = matchingProperties; bestMatch = candidate; } } return(bestMatch); }
/// <summary> /// Complete creation of this response type by merging all sources of data. /// </summary> public void Complete() { this.TypeData = this.ModuleData; RuntimeTypeData specificationData = FindBestMatchingSpecification(this.ModuleData.Type); if (specificationData != null) { this.TypeData.MergeWith(specificationData); } }
public RuntimeTypeData Clone() { RuntimeTypeData data = new RuntimeTypeData(); data.Type = this.Type; foreach (RuntimeTypeData d in this.CollectionTypes) { data.CollectionTypes.Add(d.Clone()); } foreach (string key in this.Properties.Keys) { data.Properties[key] = this.Properties[key].Clone(); } return(data); }
public void MergeWith(RuntimeTypeData newData) { if (newData.Type != null) { this.Type = newData.Type; } if (newData.CollectionTypes != null) { this.CollectionTypes = newData.CollectionTypes; } if (newData.Properties != null) { this.Properties = newData.Properties; } }
private void CompleteRuntimeDataType(RuntimeTypeData data) { // At this point, data should have its Type filled in. In case it isn't, let's let an exception bubble up as an error Type netType = data.Type; Dictionary <string, Type> propertyTypes = new Dictionary <string, Type>(); foreach (PropertyInfo pi in netType.GetProperties()) { propertyTypes[pi.Name.ToLowerInvariant()] = pi.PropertyType; } foreach (string propertyName in data.Properties.Keys) { ParameterData propertyData = data.Properties[propertyName]; if (propertyData.Type != null) { // Sometimes the Name property isn't set - propertyName is correct in this case if (String.IsNullOrEmpty(propertyData.Name)) { propertyData.Name = propertyName; } RuntimeTypeData propertyTypeData = propertyData.Type; if (propertyTypeData.Type == null) { // Find the type of this property if (propertyTypes.ContainsKey(propertyName)) { propertyTypeData.Type = propertyTypes[propertyName]; // Recursively fill in the properties of this type CompleteRuntimeDataType(propertyTypeData); } } } } }
private RuntimeTypeData GetRuntimeTypeDataFromDefinition(string definitionName, JsonPathFinder definitionNode, Dictionary <string, RuntimeTypeData> cache, JsonPathFinder rootDoc) { // 0. Check if the type already exists if (cache.ContainsKey(definitionName)) { return(cache[definitionName]); } RuntimeTypeData data = new RuntimeTypeData(); cache[definitionName] = data; // 1. Get properties node JsonPathFinder propertiesNode = definitionNode.Find(new JsonQueryBuilder().Property("properties")).SingleOrDefault(); if (propertiesNode != null) { // 2. Read flat properties foreach (JsonPathFinder propertyContainerNode in propertiesNode.Children()) { JsonPathFinder propertyNode = propertyContainerNode.OpenContainer(); // First things first - check if the property is renamed JsonPathFinder clientNameNode = propertyNode.Find(new JsonQueryBuilder().Property("x-ms-client-name")).SingleOrDefault(); string clientName = String.Empty; if (clientNameNode != null) { clientName = clientNameNode.GetValue <string>().ToLowerInvariant(); } string jsonName = propertyNode.Key.ToLowerInvariant(); string rawJsonName = propertyNode.Key; // 5 cases: // 1: Property contains a $ref node AND property contains an x-ms-client-flatten node // 2: Property contains only a $ref node // 3: Property is of type object and contains additionalProperties (not sure how to handle this yet) // 4: Property is of type array (decode the individual items type) // 5: Property is a simple property JsonPathFinder refNode = propertyNode.Find(new JsonQueryBuilder().Property("$ref")).SingleOrDefault(); JsonPathFinder flattenNode = propertyNode.Find(new JsonQueryBuilder().Property("x-ms-client-flatten")).SingleOrDefault(); JsonPathFinder typeNode = propertyNode.Find(new JsonQueryBuilder().Property("type")).SingleOrDefault(); JsonPathFinder additionalPropertiesNode = propertyNode.Find(new JsonQueryBuilder().Property("additionalProperties")).SingleOrDefault(); JsonPathFinder arrayItemsRefNode = propertyNode.Find(new JsonQueryBuilder().Property("items").Property("$ref")).SingleOrDefault(); JsonPathFinder propertiesSubNode = propertyNode.Find(new JsonQueryBuilder().Property("properties")).SingleOrDefault(); if (refNode != null) { JsonPathFinder subDefinition = rootDoc.Find(new JsonQueryBuilder(refNode.GetValue <string>())).Single(); RuntimeTypeData referencedTypeData = GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc); if (flattenNode != null && flattenNode.GetValue <bool>()) { // Push the properties of the referenced type to this type // If it's flattened, the jsonName should actually be flatJsonName foreach (string propertyName in referencedTypeData.Properties.Keys) { data.Properties[propertyName] = referencedTypeData.Properties[propertyName].Clone(); data.Properties[propertyName].SubNodeName = "properties"; } } else { // The referenced type is simply the type of this property data.Properties[jsonName] = new ParameterData() { Name = clientName, JsonName = jsonName, RawJsonName = rawJsonName, Type = referencedTypeData }; } } else if (typeNode != null && typeNode.GetValue <string>().ToLowerInvariant().Equals("object") && additionalPropertiesNode != null) { // No converter required, this will probably be a Dictionary<string, something> data.Properties[jsonName] = new ParameterData() { Name = clientName, JsonName = jsonName, RawJsonName = rawJsonName }; // TODO: Support complex object as the additionalProperties type. Don't have a good example if this yet... } else if (typeNode != null && typeNode.GetValue <string>().ToLowerInvariant().Equals("array")) { // No converter required data.Properties[jsonName] = new ParameterData() { Name = clientName, JsonName = jsonName, RawJsonName = rawJsonName }; // When this is an array, we should add the type of the items in the array so that a converter is registered later if (arrayItemsRefNode != null) { JsonPathFinder subDefinition = rootDoc.Find(new JsonQueryBuilder(arrayItemsRefNode.GetValue <string>())).Single(); data.Properties[jsonName].Type = new RuntimeTypeData(); data.Properties[jsonName].Type.CollectionTypes.Add(GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc)); } } else if (propertiesSubNode != null) { // Not sure what to do with definition name yet // The actual Type of the anonymous type will be filled in by postprocessing // TODO: The properties of the subtype might be prefixed with "properties." data.Properties[jsonName] = new ParameterData() { Name = clientName, JsonName = jsonName, RawJsonName = rawJsonName, Type = GetRuntimeTypeDataFromDefinition(Guid.NewGuid().ToString(), propertyNode, cache, rootDoc) }; } else { // The last case should be the case of a simple property - assign the name and jsonName and let the type be inferred later data.Properties[jsonName] = new ParameterData() { Name = clientName, JsonName = jsonName, RawJsonName = rawJsonName, Type = new RuntimeTypeData() }; } } } // 3. Read allOf node JsonPathFinder allOfNode = definitionNode.Find(new JsonQueryBuilder().Property("allOf")).SingleOrDefault(); if (allOfNode != null) { // Get all $ref's foreach (JsonPathFinder refNode in allOfNode.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref"))) { JsonPathFinder subDefinition = rootDoc.Find(new JsonQueryBuilder(refNode.GetValue <string>())).Single(); RuntimeTypeData referencedTypeData = GetRuntimeTypeDataFromDefinition(subDefinition.Path, subDefinition, cache, rootDoc); // Push the properties of the referenced type to this type foreach (string propertyName in referencedTypeData.Properties.Keys) { data.Properties[propertyName] = referencedTypeData.Properties[propertyName]; } } } return(data); }
private Dictionary <string, ParameterData> GetParameterMetadata(string parameterPath, JsonPathFinder parameter, Dictionary <string, RuntimeTypeData> definitionsCache, Dictionary <string, Dictionary <string, ParameterData> > parameterCache, JsonPathFinder rootDoc) { if (parameterCache.ContainsKey(parameterPath)) { // We've already transformed this parameter into one or more parameters and found the correct module name return(parameterCache[parameterPath]); } Dictionary <string, ParameterData> ret = new Dictionary <string, ParameterData>(); parameterCache[parameterPath] = ret; // 1. Check if it's a ref JsonPathFinder refNode = parameter.Find(new JsonQueryBuilder().Property("$ref")).SingleOrDefault(); if (refNode != null) { // A direct ref should reference a parameter object string refParameterPath = refNode.GetValue <string>(); return(GetParameterMetadata(refParameterPath.ToLowerInvariant(), rootDoc.Find(new JsonQueryBuilder(refParameterPath)).Single(), definitionsCache, parameterCache, rootDoc)); } else { // 2. Get the name and jsonName string jsonName = String.Empty; string clientName = String.Empty; JsonPathFinder jsonNameNode = parameter.Find(new JsonQueryBuilder().Property("name")).SingleOrDefault(); JsonPathFinder clientNameNode = parameter.Find(new JsonQueryBuilder().Property("x-ms-client-name")).SingleOrDefault(); if (jsonNameNode != null) { jsonName = jsonNameNode.GetValue <string>().ToLowerInvariant(); } if (clientNameNode != null) { clientName = clientNameNode.GetValue <string>().ToLowerInvariant(); } // 2. Check if it's a flatten JsonPathFinder flattenNode = parameter.Find(new JsonQueryBuilder().Property("x-ms-client-flatten")).SingleOrDefault(); JsonPathFinder schemaRefNode = parameter.Find(new JsonQueryBuilder().RecursiveDescent().Property("$ref")).SingleOrDefault(); if (flattenNode != null) { // Get the ref node under the schema node - using recursive descent from the parameter node should work // This should be a definitions object string definitionPath = schemaRefNode.GetValue <string>(); RuntimeTypeData definitionData = definitionsCache[definitionPath]; // Don't think there will be a flatten of an object that's just an array? foreach (string propertyName in definitionData.Properties.Keys) { ParameterData data = definitionData.Properties[propertyName]; if (!String.IsNullOrEmpty(data.Name)) { ret[data.Name] = data; } else { ret[data.JsonName] = data; } } } else { // 3. Just a regular parameter - check if it defines a definition as the type ParameterData data = new ParameterData(); data.Name = clientName; data.JsonName = jsonName; if (schemaRefNode != null) { string definitionPath = schemaRefNode.GetValue <string>().ToLowerInvariant(); data.Type = definitionsCache[definitionPath]; } else { // Type with no properties, and the Type itself will be filled in later data.Type = new RuntimeTypeData(); } if (!String.IsNullOrEmpty(clientName)) { ret[clientName] = data; } else { ret[jsonName] = data; } } } return(ret); }