• [原]Google API学习2:Google API稍深入一步


    严格的来说,这篇文章是Google API学习1:Silverlight显示Google Calendar中的内容,但是又觉得今天的认识比昨天稍深入了一步,再加到昨天的文章似乎有点不能突出重点,因此有必要新增一篇。

     

    昨天使用了Google API的.NET Client Library对Google Calendar进行了访问,但是一直觉得有些地方不太满意,最主要的原因可能是没有弄明白Google API的内部原理吧,老是觉得心里不踏实,今天又重新把Google API的安装文件夹看了一遍,发现在C:\Program Files\Google\Google Data API SDK\Sources\Library下面似乎有Google API的源代码,于是尝试在原来的解决方案中使用这些源代码,通过调试的方式发现Google API的实现细节,很幸运,添加了这些项目的源代码,程序居然能成功编译,运行后也能正常加载Google Calendar的数据!值得一提的是,因为我只是研究Google Calendar,所以只添加了Core Client、Calendar、Common Data Extensions三个项目。

     

    昨天的学习最让我感到不满的是在获取所有日历的方法内部,通过CalendarService和CalendarQuery我获取到了包含所有Calendar的CalendarFeed对象,但是为了使客户端能获取CalendarFeed对象,我使用SyndicationFeed对象给“中转”了一下,最终GetAllCalendars返回的是SyndicationFeedFormatter类型的对象。

     

                CalendarService service = new CalendarService("think8848");
                service.setUserCredentials(
    "CleverSoftDev@Gmail.com""think8848");

                CalendarQuery query 
    = new CalendarQuery("http://www.google.com/calendar/feeds/default/allcalendars/full");
                CalendarFeed calendars 
    = service.Query(query) as CalendarFeed;

                MemoryStream ms 
    = new MemoryStream();
                calendars.SaveToXml(ms);
                ms.Seek(
    3, SeekOrigin.Begin);
                SyndicationFeed resultCalendars 
    = SyndicationFeed.Load(XmlReader.Create(ms));
                
    return new Atom10FeedFormatter(resultCalendars);

     

    在今天下午的尝试中,我又尝试返回一个string值,让客户端去处理这个string值,然后得到AtomEntry的XML数据,再使用这些XML数据构造一个SyndicationFeed对象。

    服务器端

    public string GetAllCalendars()
            {
                CalendarService service 
    = new CalendarService("think8848");
                service.setUserCredentials(
    "CleverSoftDev@Gmail.com""think8848");

                CalendarQuery query 
    = new CalendarQuery("http://www.google.com/calendar/feeds/default/allcalendars/full");
                CalendarFeed calendars 
    = service.Query(query) as CalendarFeed;

                MemoryStream ms 
    = new MemoryStream();
                calendars.SaveToXml(ms);

                ASCIIEncoding encoding 
    = new ASCIIEncoding();
                
    string atomEntry = encoding.GetString(ms.ToArray());
                atomEntry 
    = atomEntry.Substring(3, atomEntry.Length - 3);
                
    return atomEntry;
            }

    客户端

     

            private void request_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
            {
                
    using (var tmpReader = XmlReader.Create(new StringReader(e.Result)))
                {
                   tmpReader.ReadStartElement();
                   
    using (var reader = XmlReader.Create(new StringReader(tmpReader.Value)))
                   {
                       var feed 
    = SyndicationFeed.Load(reader);

                       
    this.dgCalendar.ItemsSource = feed.Items;
                   }
                }            
            }

    其实今天的这个方法更CUO,基本上没有任何意义,因为服务本来应该是完成自已应该完成的功能,而不应该把自已的职责交给客户端来完成,这样做虽然少了CalendarFeed转换SyndicationFeed的动作,但不见得效率会提高多少,而且从结构上来讲,实在是不值得。晚上想了好久,写这些代码出来,唯一的收获就是把使用XmlReader对象熟悉了一点。

    晚上的想法比较简单,能不能直接把CalendarFeed输出,使客户端能接受到AtomEntry呢,第一步想到的是,如果在Google API提供一个方法,用获取到的CalendarFeed生成一个类似于SyndicationFeedFormatter的对象那就最好了,找了半天,没有找到。第二步想到的是,能不能找到生成CalendarFeed之前的AtomEntry,然后使用这个XML数据生成一个SyndicationFeedFormatter对象,通过跟踪,发现Google API内部生成CalendarFeed是使用如下代码(主要部分):

            protected void ParseSource(XmlReader reader, AtomSource source)
            {
                Tracing.Assert(reader 
    != null"reader should not be null");
                
    if (reader == null)
                {
                    
    throw new ArgumentNullException("reader"); 
                }
                Tracing.Assert(source 
    != null"source should not be null");
                
    if (source == null)
                {
                    
    throw new ArgumentNullException("source"); 
                }

                Tracing.TraceCall();
                
    //
                
    // atomSource =
                
    //    element atom:source {
                
    //       atomCommonAttributes,
                
    //       (atomAuthor?
                
    //        & atomCategory*
                
    //        & atomContributor*
                
    //        & atomGenerator?
                
    //        & atomIcon?
                
    //        & atomId?
                
    //        & atomLink*
                
    //        & atomLogo?
                
    //        & atomRights?
                
    //        & atomSubtitle?
                
    //        & atomTitle?
                
    //        & atomUpdated?
                
    //        & extensionElement*)
                
    //  this will also parse the gData extension elements.
                
    //    }

                
    int depth = -1
                ParseBasicAttributes(reader, source);

                
    while (NextChildElement(reader, ref depth))
                {
                    
    object localname = reader.LocalName; 
                    AtomFeed feed 
    = source as AtomFeed; 
                    
    if (IsCurrentNameSpace(reader, BaseNameTable.NSAtom))
                    {
                        
    if (localname.Equals(this.nameTable.Title))
                        {
                            source.Title 
    = ParseTextConstruct(reader, source);
                        }
                        
    else if (localname.Equals(this.nameTable.Updated))
                        {
                            source.Updated 
    = DateTime.Parse(Utilities.DecodedValue(reader.ReadString()), CultureInfo.InvariantCulture);
                        }
                        
    else if (localname.Equals(this.nameTable.Link))
                        {
                            source.Links.Add(ParseLink(reader, source)); 
                        }
                        
    else if (localname.Equals(this.nameTable.Id))
                        {
                            source.Id 
    = source.CreateAtomSubElement(reader, thisas AtomId;
                            ParseBaseLink(reader, source.Id);
                        }
                        
    else if (localname.Equals(this.nameTable.Icon))
                        {
                            source.Icon 
    = source.CreateAtomSubElement(reader, thisas AtomIcon;
                            ParseBaseLink(reader, source.Icon);
                        }
                        
    else if (localname.Equals(this.nameTable.Logo))
                        {
                            source.Logo 
    = source.CreateAtomSubElement(reader, thisas AtomLogo;
                            ParseBaseLink(reader, source.Logo);
                        }
                        
    else if (localname.Equals(this.nameTable.Author))
                        {
                            source.Authors.Add(ParsePerson(reader, source));
                        }
                        
    else if (localname.Equals(this.nameTable.Contributor))
                        {
                            source.Contributors.Add(ParsePerson(reader, source));
                        }
                        
    else if (localname.Equals(this.nameTable.Subtitle))
                        {
                            source.Subtitle 
    = ParseTextConstruct(reader, source);
                        }
                        
    else if (localname.Equals(this.nameTable.Rights))
                        {
                            source.Rights 
    = ParseTextConstruct(reader, source);
                        }
                        
    else if (localname.Equals(this.nameTable.Generator))
                        {
                            source.Generator 
    = ParseGenerator(reader, source); 
                        }
                        
    else if (localname.Equals(this.nameTable.Category))
                        {
                            
    // need to make this another colleciton
                            source.Categories.Add(ParseCategory(reader, source)); 
                        }
                        
    else if (feed != null && localname.Equals(this.nameTable.Entry))
                        {
                            ParseEntry(reader);
                        }
                        
    // this will either move the reader to the end of an element
                        
    // if at the end, to the start of a new one. 
                        reader.Read();
                    }
                    
    else if (feed != null && IsCurrentNameSpace(reader, BaseNameTable.gBatchNamespace))
                    {
                        
    // parse the google batch extensions if they are there
                        ParseBatch(reader, feed); 
                    }
                    
    else if (feed != null && IsCurrentNameSpace(reader, BaseNameTable.OpenSearchNamespace(this.versionInfo)))
                    {
                        
    if (localname.Equals(this.nameTable.TotalResults))
                        {
                            feed.TotalResults 
    = int.Parse(Utilities.DecodedValue(reader.ReadString()), CultureInfo.InvariantCulture);
                        }
                        
    else if (localname.Equals(this.nameTable.StartIndex))
                        {
                            feed.StartIndex 
    = int.Parse(Utilities.DecodedValue(reader.ReadString()), CultureInfo.InvariantCulture);
                        }
                        
    else if (localname.Equals(this.nameTable.ItemsPerPage))
                        {
                            feed.ItemsPerPage 
    = int.Parse(Utilities.DecodedValue(reader.ReadString()), CultureInfo.InvariantCulture);
                        }
                    }
                    
    else
                    {
                        
    // default extension parsing.
                        ParseExtensionElements(reader, source);
                    }
                }
                
    return;
            }

    值的一提的是这里的reader归根到底实际上是从HttpWebResponse生成的。

    找了很久,貌似没有简单方便的将CalendarFeed的XML数据获取到,似乎只有使用CalendarFeed.SaveToXml方法才能获取到XML数据,很无奈,又回过头来.NET Framework中找办法,一查MSDN,有了新的发现,在MSDN中发现Atom10FeedFormatter类型有一个下面这个方法

    ReadFrom 从指定的 XmlReader 实例读取 Atom 1.0 联合源。 (继承自 Atom10FeedFormatter。)

    CalendarFeed将其XML表示形式的数据写到了一个Stream中,而XmlReader又是通过一个Stream构造出来的,这就意味着在服务端的代码中不再需要SyndicationFeed对象来中转了,客户端可以直接使用使用AtomEntry了。

    于是迅速将服务端代码改写了如下样子:

                CalendarService service = new CalendarService("think8848");
                service.setUserCredentials(
    "CleverSoftDev@Gmail.com""think8848");

                CalendarQuery query 
    = new CalendarQuery("http://www.google.com/calendar/feeds/default/allcalendars/full");
                CalendarFeed calendars 
    = service.Query(query) as CalendarFeed;

                MemoryStream ms 
    = new MemoryStream();
                calendars.SaveToXml(ms);
                ms.Seek(
    3, SeekOrigin.Begin);
                Atom10FeedFormatter atomFormatter 
    = new Atom10FeedFormatter();
                atomFormatter.ReadFrom(XmlReader.Create(ms));
                
    return atomFormatter;

    客户端如下:

            private void request_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
            {
                var reader 
    = XmlReader.Create(new StringReader(e.Result));
                var feed 
    = SyndicationFeed.Load(reader);

                
    this.dgCalendar.ItemsSource = feed.Items;
            }

    运行一下,OK,结果正常。

    写到这里仍还有些地方不太明白,CalendarFeed.SaveToXml方法调用后,得到的XML数据前面会加上三个“???”,造成XML解析错误,因此不得不使用了

    ms.Seek(3, SeekOrigin.Begin);

    来解决,感觉不太好...

    还没有试过在Silverlight项目中能不能直接使用Google API,也许可以,但那不是我们公司的方向,所以也就不作研究了。

    当然,如果直接使用Web Service的方式来访问Google API就不会存在我现在所遇到的问题了,但是那样做工程就变得更复杂了,和做一个Google API Client没什么区别了。

     

  • 相关阅读:
    lombk在IDEA中报ClassNotFoundException错误
    Groovy在不同JDK版本下的性能差异
    Groovy中那些神奇注解之InheritConstructors
    Groovy中那些神奇注解之ToString
    Groovy中那些神奇注解之Memoized
    写个自己的远程桌面
    JAVA的BIT数组
    基于JDK 8的Dubbo Admin
    走进Groovy (二)
    走进Groovy (一)
  • 原文地址:https://www.cnblogs.com/think8848/p/1609872.html
Copyright © 2020-2023  润新知