【发布时间】:2012-04-09 00:09:41
【问题描述】:
我正在尝试解析 Google 联系人 API 返回的 XML
我创建了一些帮助类来让我对必要的数据进行强类型访问,但似乎无法让它们协同工作。
我创建了一个继承 XDocument 的类 GoogleDocument 和一个继承 XElement 的 GoogleContact
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
问题:
- 难道没有更简单的方法可以将所有匹配的元素转换为 GoogleContacts 吗?然后通过迭代添加每个1。似乎 GoogleContact 并不是真正的 XElement,因为它在调试器中显示为
{<entry....>}而不是<entry....>我不确定这些大括号在这里是什么意思,但它很奇怪 - 为什么我需要一次又一次地声明命名空间?有没有办法以某种方式将所有相关的命名空间传递给 GoogleContact?按照现在的方式,Google 拒绝接受返回的数据,因为所有命名空间都变为“p1”
我会很感激任何关于这个主题的建议
非常感谢
编辑
这里有一个更完整的代码示例,根据 Jon Skeet 的建议进行了更改
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
【问题讨论】:
-
为什么要从 XDocument 和 XElement 继承?这对我来说听起来是个坏主意 - 为什么不直接创建组合现有对象的类?
-
几个原因:最初是因为我过去在类似的情况下这样做但没有命名空间,而且它自然适合以 xml sn-ps 形式出现的东西。但此外,因为最后我有时需要调用
ToString以获取确切的 XML 结构以发回 Google 以获取更新 -
这些对我来说都不是很好的理由。联系人自然不是 XML 元素 - 这正是它可能的表示方式。您始终可以覆盖
ToString()(或创建一个单独的方法)来获取 XML。 -
我不想在 ToString 中重建整个 xml,因为我可以在整个代码中保留原始 xml,只添加一些属性
-
您不必全部“重建” - 您只需在仍存储
XElement的成员变量上调用ToString。重点是使用 composition 而不是 inheritance。
标签: .net xml namespaces linq-to-xml