Linq para XML para KML?
-
02-07-2019 - |
Pergunta
Eu sou um LINQ to novato XML, e um novato KML bem; Então tenha paciência comigo.
Meu objetivo é extrair marcadores individuais de um arquivo KML. Meu KML começa assim:
<?xml version="1.0" encoding="utf-8"?>
<Document xmlns="http://earth.google.com/kml/2.0">
<name>Concessions</name>
<visibility>1</visibility>
<Folder>
<visibility>1</visibility>
<Placemark>
<name>IN920211</name>
<Style>
<PolyStyle>
<color>80000000</color>
</PolyStyle>
</Style>
<Polygon>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates>11.728374,1.976421,0 11.732967,1.965322,0 11.737225,1.953161,0 11.635858,1.940812,0 11.658102,1.976874,0 11.728374,1.976421,0 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
...
Esta é, tanto quanto eu comecei:
Dim Kml As XDocument = XDocument.Load(Server.MapPath("../kmlimport/ga.kml"))
Dim Placemarks = From Placemark In Kml.Descendants("Placemark") _
Select Name = Placemark.Element("Name").Value
Até agora há boas - Kml.Descendants ( "Placemark") me dá uma enumeração vazia. O documento é carregado corretamente - porque KML.Descendants contém cada nó. Por que vale a pena essas consultas vêm acima de vazio, bem como:
Dim foo = Kml.Descendants("Document")
Dim foo = Kml.Descendants("Folder")
Pode alguém me aponte na direção certa? Os pontos de bônus para ligações a boa Linq para tutoriais XML - os que eu encontrei parada online em cenários muito simples.
Solução 2
Graças a spoon16 e Bruce Murdock por me apontar na direção certa. O código que spoon16 postou funciona, mas obriga a concatenar o namespace com cada nome único elemento, que não é tão limpo quanto eu gostaria.
Eu fiz um pouco mais procura e eu descobri como isto é suposto ser feito - este é super concisa, e eu amo o novo <...> suporte de sintaxe para referindo-se a elementos XML
Imports <xmlns:g='http://earth.google.com/kml/2.0'>
Imports System.Xml.Linq
...
Dim Kml As XDocument = XDocument.Load(Server.MapPath("../kmlimport/ga.kml"))
For Each Placemark As XElement In Kml.<g:Document>.<g:Folder>.<g:Placemark>
Dim Name As String = Placemark.<g:name>.Value
Next
Observe o : g seguindo as xmlns na primeira linha. Isto dá-lhe um atalho para se referir a isso em outro lugar namespace.
Para saber mais sobre a classe XNamespace, consulte a MSDN documentação.
Outras dicas
Isso funciona para mim em C #:
XDocument doc = XDocument.Load(@"TheFile.kml");
var q = doc.Descendants().Where(x => x.Name.LocalName == "Placemark");
Scott Hanselman tem uma solução concisa para aqueles que procuram uma solução baseada C #.
XLinq para suporte a XML em VB9
Além disso, usar XNamespace vem a calhar, em vez de apenas anexando uma string. Isto é um pouco mais formal.
// This code should get all Placemarks from a KML file
var xdoc = XDocument.Parse(kmlContent);
XNamespace ns = XNamespace.Get("http://earth.google.com/kml/2.0");
var ele = xdoc.Element(ns + "kml").Element(ns + "Document").Elements(ns + "Placemark");
Nenhuma das correções acima fez o trabalho; ver os meus comentários para mais detalhes. Eu acredito que ambos spoon16 e Bruce Murdock está no caminho certo, uma vez que o namespace é definitivamente a questão.
Depois de mais Googling me deparei com algum código em desta página que sugeriu uma solução:. apenas tira o atributo xmlns do XML original
' Read raw XML
Dim RawXml As String = ReadFile("../kmlimport/ga.kml")
' HACK: Linq to XML choking on the namespace, just get rid of it
RawXml = RawXml.Replace("xmlns=""http://earth.google.com/kml/2.0""", "")
' Parse XML
Dim Kml As XDocument = XDocument.Parse(RawXml)
' Loop through placemarks
Dim Placemarks = From Placemark In Kml.<Document>.<Folder>.Descendants("Placemark")
For Each Placemark As XElement In Placemarks
Dim Name As String = Placemark.<name>.Value
...
Next
Se qualquer um pode postar código de trabalho que trabalha com o namespace em vez de nuking-lo, eu vou com prazer dar-lhes a resposta.
Você pode precisar adicionar um espaço de nomes para o nome XElement
Dim ns as string = "http://earth.google.com/kml/2.0"
dim foo = Kml.Descendants(ns + "Document")
ignorar quaisquer erros de sintaxe, eu trabalho em c #
Você vai encontrar não pode haver uma diferença na XElement.Name
vs XElement.Name.LocalName/
Eu costumo foreach
por toda a XElements
no doc como um primeiro passo para ter certeza que eu estou usando a nomenclatura correta.
C # Aqui está um trecho do meu uso, parece que eu esqueci o {}
private string GpNamespace =
"{http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions}";
var results = admldoc.Descendants(GpNamespace +
"presentationTable").Descendants().Select(
p => new dcPolicyPresentation(p));