/// <summary> /// read the m2:count tag in the feed /// </summary> private void ReadCountValue() { Debug.Assert(this.countValue == CountStateInitial, "Count value is not in the initial state"); if (this.CurrentFeed != null && this.CurrentFeed.Count.HasValue) { this.countValue = this.CurrentFeed.Count.Value; return; } // find the first element tag while (this.reader.NodeType != XmlNodeType.Element && this.reader.Read()) { } if (this.reader.EOF) { throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent); } // the tag Should only be <feed> or <links> tag: Debug.Assert( (Util.AreSame(XmlConstants.AtomNamespace, this.reader.NamespaceURI) && Util.AreSame(XmlConstants.AtomFeedElementName, this.reader.LocalName)) || (Util.AreSame(XmlConstants.DataWebNamespace, this.reader.NamespaceURI) && Util.AreSame(XmlConstants.LinkCollectionElementName, this.reader.LocalName)), "<feed> or <links> tag expected"); // Create the XElement for look-ahead // DEVNOTE(pqian): // This is not streaming friendly! XElement element = XElement.Load(this.reader); this.reader.Close(); // Read the count value from the xelement XElement countNode = element.Descendants(XNamespace.Get(XmlConstants.DataWebMetadataNamespace) + XmlConstants.RowCountElement).FirstOrDefault(); if (countNode == null) { throw new InvalidOperationException(Strings.MaterializeFromAtom_CountNotPresent); } else { if (!long.TryParse(countNode.Value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out this.countValue)) { throw new FormatException(Strings.MaterializeFromAtom_CountFormatError); } } this.reader = new Microsoft.OData.Service.Client.Xml.XmlAtomErrorReader(element.CreateReader()); }
/// <summary>Initializes a new <see cref="AtomParser"/> instance.</summary> /// <param name="reader"><see cref="XmlReader"/> to parse content from.</param> /// <param name="entryCallback"> /// Callback invoked each time an ATOM entry is found; see the comments /// on the entryCallback field. /// </param> /// <param name="typeScheme"> /// Scheme used to find type information on ATOM category elements. /// </param> /// <param name="currentDataNamespace">The xml document's DataWeb Namespace</param> /// <param name="baseUriResolver">Interface to retrieve the baseUri to use for this EntitySetName - this will be used to convert relative uri's in the response payload to absolute uri's.</param> /// <param name="maxProtocolVersion">max protocol version that the client understands.</param> internal AtomParser(XmlReader reader, Func<XmlWrappingReader, KeyValuePair<XmlWrappingReader, AtomTag>> entryCallback, string typeScheme, string currentDataNamespace, UriResolver baseUriResolver, DataServiceProtocolVersion maxProtocolVersion) { Debug.Assert(reader != null, "reader != null"); Debug.Assert(typeScheme != null, "typeScheme != null"); Debug.Assert(entryCallback != null, "entryCallback != null"); Debug.Assert(!String.IsNullOrEmpty(currentDataNamespace), "currentDataNamespace is empty or null"); Debug.Assert(baseUriResolver != null, "baseUriResolver != null"); if (reader.Settings.NameTable != null) { // NOTE: dataNamespace is used for reference equality, and while it looks like // a variable, it appears that it will only get set to XmlConstants.DataWebNamespace // at runtime. Therefore we remove string dataNamespace as a field here. // this.dataNamespace = reader != null ? reader.Settings.NameTable.Add(context.DataNamespace) : null; reader.Settings.NameTable.Add(currentDataNamespace); } this.reader = new Microsoft.OData.Service.Client.Xml.XmlAtomErrorReader(reader); this.readers = new Stack<XmlWrappingReader>(); this.entryCallback = entryCallback; this.typeScheme = typeScheme; this.currentDataNamespace = currentDataNamespace; this.baseUriResolver = baseUriResolver; this.countValue = CountStateInitial; this.MaxProtocolVersion = maxProtocolVersion; Debug.Assert(this.kind == AtomDataKind.None, "this.kind == AtomDataKind.None -- otherwise not initialized correctly"); }
internal static KeyValuePair<XmlWrappingReader, AtomTag> XElementBuilderCallback(XmlWrappingReader reader) { Debug.Assert(reader != null, "reader != null"); XElement element = XElement.Load(reader.ReadSubtree(), LoadOptions.None); // The XElement created here will be passed to users through the DataServiceContext.ReadingEntity event. If xml:base is on the parent // element, there is no way to get to the base uri from the event handler. Links in the atom payload are relative uris, we are storing the // xml base in the tag and pass it to ReadingWritingEntityEventArgs.BaseUri so the full uris for links can be constructed. // // ReadingWritingEntityEventArgs.BaseUri contains the base uri from the current element. If the current XElement also contains a xml:base // attribute, it will be part of the uri we are passing to ReadingWritingEntityEventArgs.BaseUri. // // [Client-ODataLib-Integration] ReadingEntityEvent in astoria client gives the incorrect base uri in the event args // We will pass in the base uri of the parent element to the readingentity event. AtomTag tag = new AtomTag(element, reader.ParentBaseURI); return new KeyValuePair<XmlWrappingReader, AtomTag>(XmlWrappingReader.CreateReader(reader.ParentBaseURI, element.CreateReader()), tag); }
/// <summary> /// Parses the current reader into a new <paramref name="targetEntry"/> /// instance. /// </summary> /// <param name="targetEntry"> /// After invocation, the target entry that was created as a result /// of parsing the current reader. /// </param> private void ParseCurrentEntry(out AtomEntry targetEntry) { Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element"); // Push reader. var callbackResult = this.entryCallback(this.reader); Debug.Assert(callbackResult.Key != null, "callbackResult.Key != null"); this.readers.Push(this.reader); this.reader = callbackResult.Key; this.reader.Read(); Debug.Assert(this.reader.LocalName == "entry", "this.reader.LocalName == 'entry' - otherwise we're not reading the subtree"); targetEntry = new AtomEntry(this.MaxProtocolVersion); targetEntry.DataValues = new List<AtomContentProperty>(); targetEntry.Tag = callbackResult.Value; targetEntry.EntityDescriptor.ETag = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace); while (this.reader.Read()) { if (ShouldIgnoreNode(this.reader)) { continue; } if (this.reader.NodeType == XmlNodeType.Element) { int depth = this.reader.Depth; string elementName = this.reader.LocalName; string namespaceURI = this.reader.NamespaceURI; if (namespaceURI == XmlConstants.AtomNamespace) { if (elementName == XmlConstants.AtomCategoryElementName && targetEntry.TypeName == null) { string text = this.reader.GetAttributeEx(XmlConstants.AtomCategorySchemeAttributeName, XmlConstants.AtomNamespace); if (text == this.typeScheme && !targetEntry.TypeNameHasBeenSet) { targetEntry.TypeNameHasBeenSet = true; targetEntry.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomCategoryTermAttributeName, XmlConstants.AtomNamespace); } } else if (elementName == XmlConstants.AtomContentElementName) { this.ParseCurrentContent(targetEntry); } else if (elementName == XmlConstants.AtomIdElementName && targetEntry.Identity == null) { // The .Identity == null check ensures that only the first id element is processed. string idText = ReadElementStringForText(this.reader); WebUtil.ValidateIdentityValue(idText); targetEntry.Identity = idText; } else if (elementName == XmlConstants.AtomLinkElementName) { this.ParseCurrentLink(targetEntry); } } else if (namespaceURI == XmlConstants.DataWebMetadataNamespace) { if (elementName == XmlConstants.AtomPropertiesElementName) { if (targetEntry.MediaLinkEntry.HasValue && !targetEntry.MediaLinkEntry.Value) { // This means we saw a non-empty <atom:Content> element but now we have a Properties element // that also carries properties throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed); } targetEntry.MediaLinkEntry = true; if (!this.reader.IsEmptyElement) { this.ReadCurrentProperties(targetEntry.DataValues); } } else if (elementName == XmlConstants.ActionElementName || elementName == XmlConstants.FunctionElementName) { this.ReadOperationDescriptor(targetEntry); } } CommonUtil.SkipToEndAtDepth(this.reader, depth); } } if (targetEntry.Identity == null) { throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement); } this.reader = this.readers.Pop(); }