2012-03-24 2 views
1

им пытается разобрать XML, возвращаемого Google Contacts APIнаследуя XDocument/XElement пространства имен (Google Contacts API)

я сделал некоторые вспомогательные классы, чтобы дать мне сильно типизированный доступ к необходимым данным, но не может, кажется, чтобы они работали в тандеме.

я сделал класс, который наследуется GoogleDocument XDocument и GoogleContact, который наследуется XElement

Class GoogleDocument 
Inherits XDocument 
Dim xnsAtom = XNamespace.Get("http://www.w3.org/2005/Atom") 
Sub New() 
    MyBase.new() 
End Sub 
Sub New(other As XDocument) 
    MyBase.New(other) 
End Sub 
ReadOnly Property Entries As IEnumerable(Of GoogleContact) 
    Get 
     Dim feed = Element(xnsAtom + "feed") 
     Dim ret = New List(Of GoogleContact) 
     For Each e In feed.Elements(xnsAtom + "entry") 
      ret.Add(New GoogleContact(e)) 
     Next 
     Return ret.AsEnumerable 
    End Get 
End Property 
End Class 

Class GoogleContact 
Inherits XElement 
Dim xnsGd = XNamespace.Get("http://schemas.google.com/g/2005") 
Dim xnsAtom = XNamespace.Get("http://www.w3.org/2005/Atom") 
Dim xnsApp = XNamespace.Get("http://www.w3.org/2007/app") 
Sub New(other As XElement) 
    MyBase.new(other) 
End Sub 
ReadOnly Property ETag As String 
    Get 
     Return Attribute(xnsGd + "etag").Value 
    End Get 
End Property 
ReadOnly Property ContactID As Integer 
    Get 
     Dim uri = Element(xnsAtom + "id").Value 
     Return uri.Substring(uri.LastIndexOf("/") + 1) 
    End Get 
End Property 
ReadOnly Property Edited As DateTime 
    Get 
     Return Date.Parse(Element(xnsApp + "edited").Value) 
    End Get 
End Property 
End Class 

вопросы:

  1. не является более простой способ, чтобы преобразовать все соответствующие элементы GoogleContacts? затем добавив каждый 1 через итерацию. также кажется, что GoogleContact на самом деле не является XElement, как в отладчике, он отображается как {<entry....>} вместо <entry....> им не уверен, что здесь означают эти фигурные скобки, но его нечетные
  2. Почему мне нужно снова и снова объявлять пространства имен? не существует способа каким-то образом перейти на GoogleContact на все соответствующие пространства имен? как его сейчас, Google отказывается принимать данные обратно, как и все пространства имен становятся «p1»

я был бы признателен за любые советы по этому вопросу

спасибо

EDIT

вот более полный пример кода, с изменениями в предложения Джона Скита

Imports System.Net 
Imports System.Text 
Imports System.IO 
Imports System.Collections.Specialized 
Imports System.Runtime.CompilerServices 

Module Module1 

Dim GUserName As String 
Dim GPassword As String 
Sub Main() 
    Dim authRequest As HttpWebRequest = HttpWebRequest.Create("https://www.google.com/accounts/ClientLogin") 
    authRequest.KeepAlive = True 
    authRequest.ContentType = "application/x-www-form-urlencoded" 
    authRequest.Method = "POST" 
    Dim encoder = New ASCIIEncoding 
    Dim encodedData = encoder.GetBytes("Email=" & GUserName & "&Passwd=" & GPassword & "&source=Consultor&service=cp&accountType=HOSTED_OR_GOOGLE") 
    authRequest.ContentLength = encodedData.Length 
    Dim requestStream = authRequest.GetRequestStream 
    requestStream.Write(encodedData, 0, encodedData.Length) 
    requestStream.Close() 
    Dim authResponse = authRequest.GetResponse 
    Dim readStream = New StreamReader(authResponse.GetResponseStream, encoder) 
    Dim body = readStream.ReadToEnd 
    Dim tokens = TextCollection(body, "=", Chr(10)) 
    Dim req2 = New GoogleClient(tokens("auth")) 
    body = req2.GetString("default/full?max-results=5000") 
    Dim gDoc = New GoogleDocument(XDocument.Parse(body)) 
    Dim dcx = DBEntities() 
    Dim pers = dcx.Persons 
    For Each ge In gDoc.Entries 
     Dim entry = ge 
     Dim id As String = entry.ContactID 
     Dim p As Object '= (From x In pers Where x.GoogleCode = id).FirstOrDefault' cant ompile iin this demo 
     If p Is Nothing Then Exit For 
     If entry.Edited > p.LastEdit Then 
      p.GoogleCode = entry.ContactID 
      dcx.SaveChanges() 
     Else 
      Dim updClient = New GoogleClient(tokens("auth")) 
      updClient.ETag = entry.ETag 
      Dim updResp = updClient.PutString("http://www.google.com/m8/feeds/contacts/" & GUserName & "/base/" & entry.ContactID, entry.UpdateXml) 
     End If 
    Next 
End Sub 
Class GoogleClient 
    Inherits WebClient 
    Property ETag As String 

    Const UrlStart = "https://www.google.com/m8/feeds/contacts/" 
    Sub New(AuthToken As String) 
     Headers.Add("Content-Type", "application/atom+xml; charset=UTF-8") 
     Headers.Add("User-Agent", "G-Consultor/GDataGAuthRequestFactory-CS-Version=1.9.0.23118--IEnumerable") 
     Headers.Add("Authorization", "GoogleLogin auth=" & AuthToken) 
     Headers.Add("GData-Version", "3.0") 
    End Sub 
    Function GetString(Path As String) As String 
     Return DownloadString(UrlStart & Path) 
    End Function 
    Public Function PutString(address As String, data As String) As String 
     If ETag <> "" Then 
      Headers.Add("Etag", ETag) 
      Headers.Add("If-Match", ETag) 
     End If 
     Return UploadString(address, "PUT", data) 
    End Function 
End Class 
Function TextCollection(Text As String, FieldDelimiter As String, Optional RowDelimiter As String = vbCrLf) As NameValueCollection 
    Text = Text.RightCut(RowDelimiter) 
    Dim ret = New NameValueCollection 
    Dim rows = Text.Split(RowDelimiter) 
    For Each cl In rows 
     ret.Add(cl.Substring(0, cl.IndexOf(FieldDelimiter)), cl.Substring(cl.IndexOf(FieldDelimiter) + FieldDelimiter.Length)) 
    Next 
    Return ret 
End Function 
Class GoogleDocument 
    Inherits XDocument 
    Dim xnsAtom = XNamespace.Get("http://www.w3.org/2005/Atom") 
    Sub New() 
     MyBase.new() 
    End Sub 
    Sub New(other As XDocument) 
     MyBase.New(other) 
    End Sub 
    ReadOnly Property Entries As IEnumerable(Of GoogleContact) 
     Get 
      Dim feed = Element(xnsAtom + "feed") 
      Dim ret = New List(Of GoogleContact) 
      For Each e In feed.Elements(xnsAtom + "entry") 
       ret.Add(New GoogleContact(e)) 
      Next 
      Return ret.AsEnumerable 
     End Get 
    End Property 
End Class 
Function DBEntities() As Object 'really should return my EF data model 
    Return Nothing 
End Function 
<Extension()> Function RightCut(value As String, CutString As String) As String 
    If Right(value, CutString.Length) = CutString Then value = value.Substring(0, value.Length - CutString.Length) 
    Return value 
End Function 
Class GoogleContact 
    Dim xnsGd = XNamespace.Get("http://schemas.google.com/g/2005") 
    Dim xnsAtom = XNamespace.Get("http://www.w3.org/2005/Atom") 
    Dim xnsApp = XNamespace.Get("http://www.w3.org/2007/app") 
    Dim xContact As XElement 
    Sub New(entry As XElement) 
     xContact = entry 
    End Sub 
    ReadOnly Property ETag As String 
     Get 
      Return xContact.Attribute(xnsGd + "etag").Value 
     End Get 
    End Property 
    ReadOnly Property ContactID As Integer 
     Get 
      Dim uri = xContact.Element(xnsAtom + "id").Value 
      Return uri.Substring(uri.LastIndexOf("/") + 1) 
     End Get 
    End Property 
    ReadOnly Property Edited As DateTime 
     Get 
      Return Date.Parse(xContact.Element(xnsApp + "edited").Value) 
     End Get 
    End Property 

    ReadOnly Property UpdateXml 
     Get 
      Return "<?xml version=""1.0"" encoding=""utf-8""?>" & xContact.ToString 
     End Get 
    End Property 

    Overrides Function ToString() As String 
     Return xContact.ToString 
    End Function 
End Class 
End Module 
+0

Почему вы унаследовав от XDocument и XElement? Это звучит как плохая идея для меня - почему бы просто не создать классы, которые * составляют * существующий объект? –

+0

несколько причин: изначально потому, что я сделал это в прошлом для подобных ситуаций, но без пространств имен, а также его естественную подгонку для чего-то, что входит в xml-фрагменты. но более того, потому что в конце мне иногда нужно вызывать 'ToString', чтобы получить точную структуру XML, чтобы отправить обратно в Google для обновлений. –

+0

Ни один из них не звучит как повод для меня. Контакт, естественно, не является элементом XML - это то, как он может быть представлен *. Вы всегда можете переопределить 'ToString()' (или создать отдельный метод), чтобы вернуть XML. –

ответ

2

Это все еще не совсем ясно, что на самом деле происходит Неправильно - если вы укажете правильное пространство имен везде, все должно быть хорошо. Однако вам не нужно повторять имена повсеместно - вы можете захотеть создать общие поля readonly типа XName, чтобы избежать опечаток. Я бы также использовал неявное преобразование от String до XName для простоты. В C# я пишу это как что-то вроде:

private static readonly XNamespace AtomNs = "http://www.w3.org/2005/Atom"; 
private static readonly XNamespace GoogleDataNs = 
    "http://schemas.google.com/g/2005"; 
private static readonly XNamespace AppNs = "http://www.w3.org/2007/app"; 

// You should work out where to put these, and their visibility 
public static readonly XName FeedElementName = AtomNs + "feed"; 
public static readonly XName EntryElementName = AtomNs + "entry"; 
public static readonly XName ETagAttributeName = GoogleDataNs + "etag"; 
// etc 

Затем вы можете использовать эту «константу» везде - это не дела, что у них есть пространство имен; что в конечном итоге не используется в вашем коде, потому что вы просто ссылаетесь на «имя элемента» или «имя атрибута» соответствующим образом.

В общих чертах, способы улучшения кода:

  • я бы до сих пор предлагают использовать .NET API, если вы можете; вы говорите, что «не нашли их очень полезными», но это может означать, что вы не исследовали их достаточно долго. Как только вы их повесите, они, скорее всего, сэкономит вам время.
  • Я бы не использовал любое наследование в коде, который вы указали, - нет необходимости извлекать из WebClient, XDocument или XElement. Ваш код будет более понятным без наследования здесь, так как вы можете создать только те элементы, которые имеют отношение к объекту, который вы пытаетесь моделировать.
  • Вы можете использовать LINQ для запроса XDocument и создать коллекцию контактов.Например (C# снова, но VB будет аналогично):

    List<GoogleContact> = document.Root 
          .Elements(GoogleContact.EntryElementName) 
          .Select(element => new GoogleContact(element)) 
          .ToList(); 
    
+0

плохо спросите вас открыто, на данный момент, поскольку мы можем быть очень близки к выяснению этого, подумайте, я должен сбросить все и попробовать свои DLL? я думаю, если и так скажут, больно идет так. Я не хочу тратить ваше время. но, если у меня может быть свой собственный код, я понимаю это. Я также ценю, что 'LINQ' вывешен в конце. о наследовании, мне так нравится, но я не думаю, что это имеет значение. но все это не помогает нам вводить им пространства имен, где это необходимо? что ты предлагаешь? спасибо за все это время и за ваше понимание (PS. не стесняйтесь публиковать сообщения на C# –

+0

@Yisman: Да, вы должны потратить немного времени на библиотеки, которые предназначены для упрощения. И да, часть наследования несколько ортогонален ко всему остальному, но я все равно настоятельно рекомендую вам внимательно обдумать это (не только здесь, но и где-то еще). –

+0

ОК, так что теперь не принимайте ваш ответ, пока я снова его попробую. не получится, посмотрим, спасибо за все твои работы. –

Смежные вопросы