наследование проблем с пространством имен xdocument / xelement (API контактов Google) - PullRequest
1 голос
/ 25 марта 2012

я пытаюсь разобрать XML, возвращенный API контактов Google

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

я создал класс 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"

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

большое спасибо

РЕДАКТИРОВАТЬ

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

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

1 Ответ

2 голосов
/ 01 апреля 2012

До сих пор не совсем ясно, что на самом деле происходит неправильно - если вы везде указываете правильное пространство имен, все должно быть хорошо. Однако вам не нужно повторять имена повсюду - вы можете создать общие поля только для чтения типа 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();
    
...