| /// <summary> |
| /// Class used to parse the Content section of the feed to return the properties data and metadata |
| /// </summary> |
| |
| namespace DataJS.Tests |
| { |
| using System; |
| using System.Collections.Generic; |
| using System.IO; |
| using System.Linq; |
| using System.ServiceModel.Syndication; |
| using System.Spatial; |
| using System.Xml; |
| using System.Xml.Linq; |
| |
| public static class AtomReader |
| { |
| const string atomXmlNs = "http://www.w3.org/2005/Atom"; |
| const string gmlXmlNs = "http://www.opengis.net/gml"; |
| const string odataRelatedPrefix = "http://schemas.microsoft.com/ado/2007/08/dataservices/related"; |
| const string odataRelatedLinksPrefix = "http://schemas.microsoft.com/ado/2007/08/dataservices/relatedlinks"; |
| const string odataXmlNs = "http://schemas.microsoft.com/ado/2007/08/dataservices"; |
| const string odataMetaXmlNs = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; |
| const string odataEditMediaPrefix = "http://schemas.microsoft.com/ado/2007/08/dataservices/edit-media"; |
| const string odataMediaResourcePrefix = "http://schemas.microsoft.com/ado/2007/08/dataservices/mediaresource"; |
| |
| const string hrefAttribute = "href"; |
| const string titleElement = "title"; |
| const string workspaceElement = "workspace"; |
| const string workspacesProperty = "workspaces"; |
| const string collectionElement = "collection"; |
| const string collectionsProperty = "collections"; |
| const string extensionsProperty = "extensions"; |
| static string baseUri = string.Empty; |
| |
| /// <summary> |
| /// Creates a service document object |
| /// </summary> |
| /// <param name="container">The XML container</param> |
| /// <param name="uri">Uri to append to the href value</param> |
| /// <returns>The service document JsonObject</returns> |
| public static JsonObject ReadServiceDocument(TextReader payload, string baseUri) |
| { |
| JsonObject jsonObject = new JsonObject(); |
| XElement container = XElement.Load(payload); |
| |
| if (container != null && container.HasElements) |
| { |
| jsonObject["workspaces"] = |
| container |
| .Elements() |
| .Where(element => element.Name.LocalName.Equals(workspaceElement)) |
| .Select(element => ReadWorkspaceObject(element, baseUri)) |
| .ToArray(); |
| |
| jsonObject["extensions"] = |
| container |
| .Elements() |
| .Where(element => !element.Name.LocalName.Equals(workspaceElement)) |
| .Select(element => ReadExtensionElement(element)) |
| .ToArray(); |
| } |
| |
| return jsonObject; |
| } |
| |
| public static JsonObject ReadEntry(TextReader payload) |
| { |
| SyndicationItem item = SyndicationItem.Load(XmlReader.Create(payload)); |
| return ReadEntry(item); |
| } |
| |
| public static JsonObject ReadFeed(TextReader payload) |
| { |
| SyndicationFeed feed = SyndicationFeed.Load(XmlReader.Create(payload)); |
| JsonObject feedData = new JsonObject(); |
| JsonObject feedMetadata = new JsonObject(); |
| |
| feedData["results"] = feed.Items.Select(item => ReadEntry(item)).ToArray(); |
| feedData["__metadata"] = feedMetadata; |
| |
| feedMetadata["feed_extensions"] = feed.AttributeExtensions.Select(pair => ReadExtension(pair)).ToArray(); |
| |
| if (feed.Id != null) |
| { |
| feedMetadata["uri"] = feed.Id; |
| feedMetadata["uri_extensions"] = new JsonObject[] { }; |
| } |
| |
| if (feed.Title != null) |
| { |
| feedMetadata["title"] = feed.Title.Text; |
| feedMetadata["title_extensions"] = GetTitleExtensions(feed.Title); |
| } |
| |
| SyndicationLink feedSelfLink = GetLink("self", feed.Links); |
| if (feedSelfLink != null) |
| { |
| feedMetadata["self"] = feedSelfLink.GetAbsoluteUri().AbsoluteUri; |
| feedMetadata["self_extensions"] = GetLinkExtensions(feedSelfLink); |
| } |
| |
| long? count = GetInlineCount(feed); |
| if (count.HasValue) |
| { |
| feedData["__count"] = count.Value; |
| } |
| |
| SyndicationLink feedNextLink = GetLink("next", feed.Links); |
| if (feedNextLink != null) |
| { |
| feedData["__next"] = feedNextLink.GetAbsoluteUri().AbsoluteUri; |
| feedMetadata["next_extensions"] = GetLinkExtensions(feedNextLink); |
| } |
| |
| return feedData; |
| } |
| |
| private static JsonObject ReadEntry(SyndicationItem item) |
| { |
| SyndicationLink entryEditLink = GetLink("edit", item.Links); |
| SyndicationCategory entryCategory = item.Categories.FirstOrDefault(); |
| |
| XElement propertiesElement = GetPropertiesElement(item); |
| JsonObject entryData = ReadObject(propertiesElement); |
| entryData = JsonObject.Merge(entryData, ReadNavigationProperties(item)); |
| entryData = JsonObject.Merge(entryData, ReadNamedStreams(item)); |
| |
| JsonObject propertiesMetadata = ReadPropertiesMetadata(propertiesElement); |
| propertiesMetadata = JsonObject.Merge(propertiesMetadata, ReadNavigationPropertiesMetadata(item)); |
| propertiesMetadata = JsonObject.Merge(propertiesMetadata, ReadNamedStreamMetadata(item)); |
| |
| JsonObject entryMetadata = new JsonObject(); |
| entryData["__metadata"] = entryMetadata; |
| entryMetadata["properties"] = propertiesMetadata; |
| |
| if (item.Id != null) |
| { |
| entryMetadata["uri"] = item.Id; |
| entryMetadata["uri_extensions"] = new JsonObject[] { }; |
| } |
| |
| if (entryCategory != null) |
| { |
| entryMetadata["type"] = entryCategory.Name; |
| entryMetadata["type_extensions"] = new JsonObject[] { }; |
| } |
| |
| if (entryEditLink != null) |
| { |
| entryMetadata["edit"] = entryEditLink.GetAbsoluteUri().AbsoluteUri; |
| entryMetadata["edit_link_extensions"] = GetLinkExtensions(entryEditLink); |
| } |
| |
| return entryData; |
| } |
| |
| private static JsonObject ReadExtension(KeyValuePair<XmlQualifiedName, string> pair) |
| { |
| return ReaderUtils.CreateExtension(pair.Key.Name, pair.Key.Namespace, pair.Value); |
| } |
| |
| private static string GetCollectionType(string type) |
| { |
| if (type != null && type.StartsWith("Collection(")) |
| { |
| int start = 11; |
| int end = type.IndexOf(")") - 11; |
| return type.Substring(start, end); |
| } |
| return null; |
| } |
| |
| /// <summary> |
| /// Find the m:properties element within a feed entry |
| /// </summary> |
| /// <param name="item">The feed entry</param> |
| /// <returns>The m:properties element</returns> |
| private static XElement GetPropertiesElement(SyndicationItem item) |
| { |
| // Check if the m:properties element is within the content element |
| XmlSyndicationContent xmlContent = item.Content as XmlSyndicationContent; |
| if (xmlContent != null) |
| { |
| XElement contentElement = XElement.Load(xmlContent.GetReaderAtContent()); |
| return contentElement.Elements().FirstOrDefault(e => e.Name == XName.Get("properties", odataMetaXmlNs)); |
| } |
| // If we're here, then we are dealing with a feed that has an MLE |
| // i.e. the m:properties element is a peer of the content element, and shows up |
| // in the elementExtensions instead |
| SyndicationElementExtension propertiesElementExtension = item.ElementExtensions.FirstOrDefault(e => e.OuterName.Equals("properties")); |
| if (propertiesElementExtension != null) |
| { |
| XNode propertiesElement = XNode.ReadFrom(propertiesElementExtension.GetReader()); |
| return (XElement)propertiesElement; |
| } |
| |
| throw new NotSupportedException("Unsupported feed entry format"); |
| } |
| |
| /// <summary> |
| /// Gets the inline count within a feed |
| /// </summary> |
| /// <param name="feed">The feed</param> |
| /// <returns>The inline count, or null if none exists</returns> |
| private static long? GetInlineCount(SyndicationFeed feed) |
| { |
| SyndicationElementExtension countElementExtension = feed.ElementExtensions.SingleOrDefault(extension => |
| extension.OuterName.Equals("count", StringComparison.OrdinalIgnoreCase) && |
| extension.OuterNamespace.Equals(odataMetaXmlNs)); |
| |
| if (countElementExtension != null) |
| { |
| XElement countElement = (XElement)XNode.ReadFrom(countElementExtension.GetReader()); |
| return XmlConvert.ToInt64(countElement.Value); |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| /// <summary> |
| /// Gets the link with the specified relationship type |
| /// </summary> |
| /// <param name="rel">Relationship type</param> |
| /// <param name="links">The set of links to search from</param> |
| /// <returns>The link with the specified relationship type, or null if none exists</returns> |
| private static SyndicationLink GetLink(string rel, IEnumerable<SyndicationLink> links) |
| { |
| return links.SingleOrDefault(link => link.RelationshipType.Equals(rel, StringComparison.InvariantCultureIgnoreCase)); |
| } |
| |
| private static IEnumerable<SyndicationLink> GetLinks(string rel, IEnumerable<SyndicationLink> links) |
| { |
| return links.Where(link => link.RelationshipType.StartsWith(rel, StringComparison.Ordinal)); |
| } |
| |
| //TODO refactor the extraction of extensions into extension elements and extension attribute methods. |
| private static JsonObject[] GetLinkExtensions(SyndicationLink link) |
| { |
| List<JsonObject> extensions = new List<JsonObject>(); |
| //TODO: fix the inclusion of title as extension. Title attribute is not required in the link element and its |
| //inclusion as an extension should be tested for the precesence of the attribute in the xml element. Unfortunately, |
| //SyndicationLink doesn't allow for accessing the underlying XML document.. perhaps using an AtomFormatter10?? |
| extensions.Add(ReaderUtils.CreateExtension("title", null, link.Title)); |
| extensions.AddRange(link.AttributeExtensions.Select(pair => ReadExtension(pair))); |
| |
| return extensions.ToArray(); |
| } |
| |
| private static JsonObject[] GetTitleExtensions(TextSyndicationContent title) |
| { |
| List<JsonObject> extensions = new List<JsonObject>(); |
| extensions.Add(ReaderUtils.CreateExtension("type", null, title.Type)); |
| extensions.AddRange(title.AttributeExtensions.Select(pair => ReadExtension(pair))); |
| |
| return extensions.ToArray(); |
| } |
| |
| /// <summary> |
| /// Gets the "type" value from a property element |
| /// </summary> |
| /// <param name="propertyElement">The property element</param> |
| /// <returns>The "type" value, or default (Edm.String) if none specified</returns> |
| private static string GetTypeAttribute(XElement propertyElement) |
| { |
| XAttribute typeAttribute = propertyElement.Attribute(XName.Get("type", odataMetaXmlNs)); |
| if (typeAttribute == null) |
| { |
| if (propertyElement.HasElements) |
| { |
| return null; |
| } |
| return "Edm.String"; |
| } |
| return typeAttribute.Value; |
| } |
| |
| private static bool HasTypeAttribute(XElement propertyElement) |
| { |
| return propertyElement.Attribute(XName.Get("type", odataMetaXmlNs)) != null; |
| } |
| |
| private static bool IsCollectionProperty(XElement propertyElement) |
| { |
| string type = GetTypeAttribute(propertyElement); |
| if (type != null && type.StartsWith("Collection(")) |
| { |
| return true; |
| |
| } |
| return propertyElement.Elements().Count(pe => pe.Name == XName.Get("element", odataXmlNs)) > 1; |
| } |
| |
| private static JsonObject ReadWorkspaceObject(XElement container, string baseUri) |
| { |
| JsonObject jsonObject = new JsonObject(); |
| |
| jsonObject["collections"] = |
| container |
| .Elements() |
| .Where(element => element.Name.LocalName.Equals("collection")) |
| .Select(element => ReadWorkspaceCollections(element, baseUri)) |
| .ToArray(); |
| |
| jsonObject["extensions"] = |
| container |
| .Elements() |
| .Where(element => |
| !(element.Name.LocalName.Equals("collection") || |
| element.Name.LocalName.Equals("title"))) |
| .Select(element => ReadExtensionElement(element)) |
| .ToArray(); |
| |
| jsonObject["title"] = |
| container |
| .Elements() |
| .Where(element => element.Name.LocalName.Equals("title")) |
| .First() |
| .Value; |
| |
| return jsonObject; |
| } |
| |
| private static JsonObject ReadWorkspaceCollections(XElement container, string baseUri) |
| { |
| JsonObject jsonObject = new JsonObject(); |
| string title = string.Empty; |
| |
| jsonObject["extensions"] = |
| container |
| .Elements() |
| .Where(element => |
| !(element.Name.LocalName.Equals(collectionElement) || |
| element.Name.LocalName.Equals(titleElement))) |
| .Select(element => ReadExtensionElement(element)) |
| .ToArray(); |
| |
| jsonObject["title"] = |
| container |
| .Elements() |
| .Where(element => element.Name.LocalName.Equals("title")) |
| .First() |
| .Value; |
| |
| IEnumerable<XAttribute> hrefAttributes = |
| container |
| .Attributes() |
| .Where(element => element.Name.LocalName.Equals("href")); |
| |
| jsonObject["href"] = baseUri + hrefAttributes.First().Value; |
| |
| return jsonObject; |
| } |
| |
| private static JsonObject ReadExtensionElement(XElement element) |
| { |
| JsonObject jsonObject = ReaderUtils.CreateExtension(element.Name.LocalName, element.BaseUri, null); |
| jsonObject.Remove("value"); |
| jsonObject["attributes"] = ReadExtensionAttributes(element); |
| jsonObject["children"] = element.Elements().Select(child => ReadExtensionElement(element)).ToArray(); |
| |
| return jsonObject; |
| } |
| |
| private static JsonObject ReadExtensionAttribute(XAttribute attribute) |
| { |
| return ReaderUtils.CreateExtension(attribute.Name.LocalName, attribute.BaseUri, attribute.Value); |
| } |
| |
| private static JsonObject[] ReadExtensionAttributes(XElement container) |
| { |
| List<JsonObject> attributes = new List<JsonObject>(); |
| foreach (XAttribute attribute in container.Attributes()) |
| { |
| attributes.Add(ReadExtensionAttribute(attribute)); |
| } |
| return attributes.ToArray(); |
| } |
| |
| private static JsonObject ReadNamedStreamMetadata(SyndicationItem item) |
| { |
| JsonObject propertiesMetadata = new JsonObject(); |
| JsonObject streamMetadata; |
| string propertyName; |
| |
| foreach (SyndicationLink link in GetLinks(odataEditMediaPrefix, item.Links)) |
| { |
| streamMetadata = new JsonObject(); |
| streamMetadata["edit_media_extensions"] = GetLinkExtensions(link); |
| streamMetadata["media_src_extensions"] = new JsonObject[0]; |
| |
| propertyName = link.RelationshipType.Substring(odataEditMediaPrefix.Length + 1); |
| propertiesMetadata[propertyName] = streamMetadata; |
| } |
| |
| foreach (SyndicationLink link in GetLinks(odataMediaResourcePrefix, item.Links)) |
| { |
| streamMetadata = new JsonObject(); |
| streamMetadata["media_src_extensions"] = GetLinkExtensions(link); |
| |
| propertyName = link.RelationshipType.Substring(odataMediaResourcePrefix.Length + 1); |
| if (propertiesMetadata.ContainsKey(propertyName)) |
| { |
| streamMetadata = JsonObject.Merge((JsonObject)propertiesMetadata[propertyName], streamMetadata); |
| } |
| propertiesMetadata[propertyName] = streamMetadata; |
| } |
| return propertiesMetadata; |
| } |
| |
| private static JsonObject ReadNamedStreams(SyndicationItem item) |
| { |
| // Not very elegant, but quick and easy, do it in two passes. |
| JsonObject streams = new JsonObject(); |
| JsonObject streamValue; |
| JsonObject mediaResource; |
| string propertyName; |
| |
| foreach (SyndicationLink link in GetLinks(odataEditMediaPrefix, item.Links)) |
| { |
| propertyName = link.RelationshipType.Substring(odataEditMediaPrefix.Length + 1); |
| streamValue = new JsonObject(); |
| mediaResource = new JsonObject(); |
| |
| streams[propertyName] = streamValue; |
| |
| streamValue["__mediaresource"] = mediaResource; |
| |
| mediaResource["edit_media"] = link.GetAbsoluteUri().AbsoluteUri; |
| mediaResource["content_type"] = link.MediaType; |
| mediaResource["media_src"] = link.GetAbsoluteUri().AbsoluteUri; |
| |
| var etagAttributeName = new XmlQualifiedName("etag", odataMetaXmlNs); |
| if (link.AttributeExtensions.ContainsKey(etagAttributeName)) |
| { |
| mediaResource["media_etag"] = link.AttributeExtensions[etagAttributeName]; |
| link.AttributeExtensions.Remove(etagAttributeName); |
| } |
| } |
| |
| foreach (SyndicationLink link in GetLinks(odataMediaResourcePrefix, item.Links)) |
| { |
| propertyName = link.RelationshipType.Substring(odataMediaResourcePrefix.Length + 1); |
| mediaResource = new JsonObject(); |
| mediaResource["content_type"] = link.MediaType; |
| mediaResource["media_src"] = link.GetAbsoluteUri().AbsoluteUri; |
| |
| if (streams.ContainsKey(propertyName)) |
| { |
| streamValue = streams[propertyName] as JsonObject; |
| mediaResource = JsonObject.Merge(streamValue["__mediaresource"] as JsonObject, mediaResource); |
| } |
| else |
| { |
| streamValue = new JsonObject(); |
| } |
| streamValue["__mediaresource"] = mediaResource; |
| streams[propertyName] = streamValue; |
| } |
| return streams; |
| } |
| |
| private static JsonObject ReadNavigationProperties(SyndicationItem item) |
| { |
| JsonObject navProperties = new JsonObject(); |
| SyndicationElementExtension inline; |
| |
| string propertyName; |
| JsonObject propertyValue = null; |
| |
| foreach (SyndicationLink link in GetLinks(odataRelatedPrefix, item.Links)) |
| { |
| propertyName = link.RelationshipType.Substring(odataRelatedPrefix.Length + 1); |
| inline = link.ElementExtensions.SingleOrDefault(e => |
| odataMetaXmlNs.Equals(e.OuterNamespace, StringComparison.Ordinal) && |
| e.OuterName.Equals("inline", StringComparison.Ordinal)); |
| |
| if (inline != null) |
| { |
| XElement inlineElement = (XElement)XNode.ReadFrom(inline.GetReader()); |
| XElement innerElement = inlineElement.Elements().FirstOrDefault(); |
| |
| if (innerElement != null) |
| { |
| // By default the inner feed/entry does not have the xml:base attribute, so we need to |
| // add it so that the parsed SyndicationFeed or SyndicationItem retains the baseUri |
| if (link.BaseUri != null) |
| { |
| innerElement.SetAttributeValue(XNamespace.Xml + "base", link.BaseUri.OriginalString); |
| } |
| |
| // We are converting to a string before creating the reader to strip out extra indenting, |
| // otherwise the reader creates extra XmlText nodes that SyndicationFeed/SyndicationItem cannot handle |
| try |
| { |
| propertyValue = ReadFeed(new StringReader(innerElement.ToString())); |
| } |
| catch (XmlException) |
| { |
| // Try with entry instead .. |
| |
| propertyValue = ReadEntry(new StringReader(innerElement.ToString())); |
| } |
| } |
| } |
| else |
| { |
| JsonObject deferred = new JsonObject(); |
| deferred["uri"] = link.GetAbsoluteUri().AbsoluteUri; |
| |
| propertyValue = new JsonObject(); |
| propertyValue["__deferred"] = deferred; |
| } |
| navProperties[propertyName] = propertyValue; |
| } |
| return navProperties; |
| } |
| |
| private static JsonObject ReadNavigationPropertiesMetadata(SyndicationItem item) |
| { |
| JsonObject propertiesMetadata = new JsonObject(); |
| JsonObject navMetadata; |
| string propertyName; |
| |
| foreach (SyndicationLink link in GetLinks(odataRelatedPrefix, item.Links)) |
| { |
| navMetadata = new JsonObject(); |
| navMetadata["extensions"] = GetLinkExtensions(link).Where(e => |
| !(string.Equals(e["name"] as string, "inline", StringComparison.Ordinal) && |
| string.Equals(e["namespaceURI"] as string, odataMetaXmlNs, StringComparison.Ordinal)) |
| ).ToArray(); |
| |
| propertyName = link.RelationshipType.Substring(odataRelatedPrefix.Length + 1); |
| propertiesMetadata[propertyName] = navMetadata; |
| } |
| |
| foreach (SyndicationLink link in GetLinks(odataRelatedLinksPrefix, item.Links)) |
| { |
| navMetadata = new JsonObject(); |
| navMetadata["associationuri"] = link.GetAbsoluteUri().AbsoluteUri; |
| navMetadata["associationuri_extensions"] = link.GetAbsoluteUri().AbsoluteUri; |
| |
| propertyName = link.RelationshipType.Substring(odataRelatedLinksPrefix.Length + 1); |
| if (propertiesMetadata.ContainsKey(propertyName)) |
| { |
| navMetadata = JsonObject.Merge(propertiesMetadata[propertyName] as JsonObject, navMetadata); |
| } |
| propertiesMetadata[propertyName] = navMetadata; |
| } |
| |
| return propertiesMetadata; |
| } |
| |
| private static JsonObject ReadPropertiesMetadata(XElement container) |
| { |
| JsonObject json = null; |
| if (container != null && container.Elements().Any(e => e.Name.NamespaceName == odataXmlNs)) |
| { |
| json = new JsonObject(); |
| foreach (XElement propertyElement in container.Elements()) |
| { |
| json[propertyElement.Name.LocalName] = ReadPropertyMetadata(propertyElement); |
| } |
| } |
| return json; |
| } |
| |
| private static JsonObject ReadPropertyMetadata(XElement property) |
| { |
| var metadata = ReaderUtils.CreateEntryPropertyMetadata(GetTypeAttribute(property)); |
| |
| if (IsCollectionProperty(property)) |
| { |
| string collectionType = GetCollectionType(GetTypeAttribute(property)); |
| if (collectionType == null) |
| { |
| metadata["type"] = "Collection()"; |
| } |
| |
| List<JsonObject> elements = new List<JsonObject>(); |
| foreach (XElement item in property.Elements(XName.Get("element", odataXmlNs))) |
| { |
| string itemType = |
| HasTypeAttribute(item) ? GetTypeAttribute(item) : |
| IsCollectionProperty(item) ? "Collection()" : collectionType; |
| |
| var itemMetadata = ReaderUtils.CreateEntryPropertyMetadata(itemType); |
| if (item.Elements().Any(e => e.Name.NamespaceName == odataXmlNs)) |
| { |
| itemMetadata["properties"] = ReadPropertiesMetadata(item); |
| } |
| elements.Add(itemMetadata); |
| } |
| metadata["elements"] = elements.ToArray(); |
| } |
| else if (property != null && property.Elements().Any(e => e.Name.NamespaceName == odataXmlNs)) |
| { |
| metadata["properties"] = ReadPropertiesMetadata(property); |
| } |
| |
| return metadata; |
| } |
| |
| /// <summary> |
| /// Creates a JsonObject from an XML container element (e.g. the m:properties element) of an OData ATOM feed entry. |
| /// </summary> |
| /// <param name="container">The XML container</param> |
| /// <param name="buildValue">Function that builds a value from a property element</param> |
| /// <returns>The JsonObject containing the name-value pairs</returns> |
| private static JsonObject ReadObject(XElement container) |
| { |
| if (container == null) |
| { |
| return null; |
| } |
| |
| var json = new JsonObject(); |
| foreach (XElement propertyElement in container.Elements()) |
| { |
| json[propertyElement.Name.LocalName] = ReadDataItem(propertyElement); |
| } |
| return json; |
| } |
| |
| private static JsonObject ReadCollectionProperty(XElement property, string typeName) |
| { |
| var collectionType = GetCollectionType(typeName); |
| |
| var json = new JsonObject(); |
| var results = new List<object>(); |
| |
| foreach (XElement item in property.Elements()) |
| { |
| object resultItem = ReadDataItem(item); |
| results.Add(resultItem); |
| |
| JsonObject complexValue = resultItem as JsonObject; |
| if (complexValue != null) |
| { |
| var metadata = complexValue["__metadata"] as JsonObject; |
| if (!string.IsNullOrEmpty(collectionType) && metadata["type"] == null) |
| { |
| metadata["type"] = collectionType; |
| } |
| } |
| } |
| |
| json["results"] = results; |
| json["__metadata"] = ReaderUtils.CreateEntryPropertyMetadata(typeName, false); |
| |
| return json; |
| } |
| |
| private static JsonObject ReadComplexProperty(XElement container, string typeName) |
| { |
| JsonObject json = ReadObject(container); |
| json["__metadata"] = ReaderUtils.CreateEntryPropertyMetadata(GetTypeAttribute(container), false); |
| return json; |
| } |
| |
| private static JsonObject ReadJsonSpatialProperty(XElement container, XElement gmlValue, bool isGeography) |
| { |
| GmlFormatter gmlFormatter = GmlFormatter.Create(); |
| GeoJsonObjectFormatter jsonformatter = GeoJsonObjectFormatter.Create(); |
| |
| bool ignoreCrc = !gmlValue.Attributes().Any(a => a.Name.LocalName == "srsName"); |
| |
| ISpatial spatialValue; |
| if (isGeography) |
| { |
| spatialValue = gmlFormatter.Read<Geography>(gmlValue.CreateReader()); |
| } |
| else |
| { |
| spatialValue = gmlFormatter.Read<Geometry>(gmlValue.CreateReader()); |
| } |
| |
| IDictionary<string, object> geoJsonData = jsonformatter.Write(spatialValue); |
| JsonObject json = new JsonObject(); |
| |
| Queue<object> geoJsonScopes = new Queue<object>(); |
| Queue<object> jsonScopes = new Queue<object>(); |
| |
| geoJsonScopes.Enqueue(geoJsonData); |
| jsonScopes.Enqueue(json); |
| |
| Func<object, object> convertScope = (scope) => |
| { |
| object newScope = |
| scope is List<object> || scope is object[] ? (object)new List<Object>() : |
| scope is IDictionary<string, object> ? (object)new JsonObject() : |
| null; |
| |
| if (newScope != null) |
| { |
| geoJsonScopes.Enqueue(scope); |
| jsonScopes.Enqueue(newScope); |
| } |
| |
| return newScope ?? scope; |
| }; |
| |
| while (jsonScopes.Count > 0) |
| { |
| if (jsonScopes.Peek() is JsonObject) |
| { |
| var currentGeoJson = (IDictionary<string, object>)geoJsonScopes.Dequeue(); |
| var currentJson = (JsonObject)jsonScopes.Dequeue(); |
| |
| foreach (var item in currentGeoJson) |
| { |
| if (!ignoreCrc || item.Key != "crs") |
| { |
| currentJson[item.Key] = convertScope(item.Value); |
| } |
| } |
| } |
| else |
| { |
| var currentGeoJson = (IEnumerable<object>)geoJsonScopes.Dequeue(); |
| var currentJson = (List<object>)jsonScopes.Dequeue(); |
| |
| foreach (var item in currentGeoJson) |
| { |
| currentJson.Add(convertScope(item)); |
| } |
| } |
| } |
| json["__metadata"] = ReaderUtils.CreateEntryPropertyMetadata(GetTypeAttribute(container), false); |
| return json; |
| } |
| |
| public static object ReadDataItem(XElement item) |
| { |
| string typeName = GetTypeAttribute(item); |
| XElement gmlRoot = item.Elements().SingleOrDefault(e => e.Name.NamespaceName == gmlXmlNs); |
| |
| if (gmlRoot != null) |
| { |
| bool isGeography = typeName.StartsWith("Edm.Geography"); |
| return ReadJsonSpatialProperty(item, gmlRoot, isGeography); |
| } |
| |
| bool isCollection = IsCollectionProperty(item); |
| if (item.HasElements || isCollection) |
| { |
| // Complex type, Collection Type: parse recursively |
| return isCollection ? ReadCollectionProperty(item, typeName) : ReadComplexProperty(item, typeName); |
| } |
| |
| // Primitive type: null value |
| XNamespace mNamespace = item.GetNamespaceOfPrefix("m"); |
| XAttribute nullAttribute = mNamespace == null ? null : item.Attribute(mNamespace.GetName("null")); |
| if (nullAttribute != null && nullAttribute.Value.Equals("true", StringComparison.InvariantCultureIgnoreCase)) |
| { |
| return null; |
| } |
| |
| // Primitive type: check type and parse value accordingly |
| string value = item.Value; |
| switch (typeName) |
| { |
| case "Edm.Byte": |
| return XmlConvert.ToByte(value); |
| case "Edm.Int16": |
| return XmlConvert.ToInt16(value); |
| case "Edm.Int32": |
| return XmlConvert.ToInt32(value); |
| case "Edm.SByte": |
| return XmlConvert.ToSByte(value); |
| case "Edm.Boolean": |
| return XmlConvert.ToBoolean(value); |
| case "Edm.Double": |
| return XmlConvert.ToDouble(value); |
| case "Edm.Single": |
| return XmlConvert.ToSingle(value); |
| case "Edm.Guid": |
| return XmlConvert.ToGuid(value); |
| case "Edm.DateTime": |
| return new JsDate(XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc)); |
| case "Edm.DateTimeOffset": |
| return new JsDate(XmlConvert.ToDateTimeOffset(value)); |
| case "Edm.Time": |
| throw new NotSupportedException(typeName + " is not supported"); |
| // Decimal and Int64 values are sent as strings over the wire. This is the same behavior as WCF Data Services JSON serializer. |
| case "Edm.Decimal": |
| case "Edm.Int64": |
| case "": |
| default: |
| return value; |
| } |
| } |
| } |
| } |