简体   繁体   English

C#无法在XMLNode中找到节点

[英]c# not able to find a node within the XMLNode

This is my kml file: 这是我的kml文件:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
  <name>Test.kml</name>
  <Folder>
    <name>Test</name>
    <open>1</open>
    <Placemark>
      <name>Placemark 1</name>
      <LookAt>
        <longitude>-150</longitude>
        <latitude>72</latitude>
        <altitude>0</altitude>
        <heading>-13.26929942603143</heading>
        <tilt>0</tilt>
        <range>33665.16192218825</range>
        <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
      </LookAt>
      <styleUrl>#m_ylw-pushpin</styleUrl>
      <Point>
        <gx:drawOrder>1</gx:drawOrder>
        <coordinates>-110.7484519621821,52.7616508182995,0</coordinates>
      </Point>
    </Placemark>
    <Placemark>
      <name>Polygon</name>
      <styleUrl>#msn_ylw-pushpin551</styleUrl>
      <Polygon>
        <tessellate>1</tessellate>
        <outerBoundaryIs>
          <LinearRing>
          <coordinates>
            -114.1205573145593,51.36318071429854,0 -114.1205787952745,51.36318006995027,0 -114.1205971712767,51.36317242116965,0 -114.1206026671322,51.36316989077702,0 -114.1206102089206,51.36316966453516,0 -114.1206306254288,51.36316432159048,0 -114.1206380046647,51.36316173522451,0 -114.1206530868876,51.36316128267593,0 -114.1206700591908,51.36316077215199,0 -114.1207186777935,51.36315339774478,0 -114.1207317114146,51.36315064137735,0 -114.1206014395037,51.36248218454789,0 -114.120595868448,51.36248353455266,0 -114.1205319409001,51.36248782272818,0 -114.1204591504232,51.36250065739123,0 -114.1203422144068,51.3624758047018,0 -114.1205573145593,51.36318071429854,0
          </coordinates>
          </LinearRing>
        </outerBoundaryIs>
      </Polygon>
    </Placemark>
  </Folder>
  <Placemark>
    <name>Placemark 1</name>
    <LookAt>
      <longitude>-150</longitude>
      <latitude>72</latitude>
      <altitude>0</altitude>
      <heading>-13.26929942603143</heading>
      <tilt>0</tilt>
      <range>33665.16192218825</range>
      <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
    </LookAt>
    <styleUrl>#m_ylw-pushpin</styleUrl>
    <Point>
      <gx:drawOrder>1</gx:drawOrder>
      <coordinates>-110.7484519621821,52.7616508182995,0</coordinates>
    </Point>
  </Placemark>
  <Placemark>
    <name>Polygon</name>
    <styleUrl>#msn_ylw-pushpin551</styleUrl>
    <Polygon>
      <tessellate>1</tessellate>
      <outerBoundaryIs>
        <LinearRing>
          <coordinates>
            -114.1205573145593,51.36318071429854,0 -114.1205787952745,51.36318006995027,0 -114.1205971712767,51.36317242116965,0 -114.1206026671322,51.36316989077702,0 -114.1206102089206,51.36316966453516,0 -114.1206306254288,51.36316432159048,0 -114.1206380046647,51.36316173522451,0 -114.1206530868876,51.36316128267593,0 -114.1206700591908,51.36316077215199,0 -114.1207186777935,51.36315339774478,0 -114.1207317114146,51.36315064137735,0 -114.1206014395037,51.36248218454789,0 -114.120595868448,51.36248353455266,0 -114.1205319409001,51.36248782272818,0 -114.1204591504232,51.36250065739123,0 -114.1203422144068,51.3624758047018,0 -114.1205573145593,51.36318071429854,0
          </coordinates>
        </LinearRing>
      </outerBoundaryIs>
    </Polygon>
  </Placemark>
 </Document>
</kml>

I want to find the name and coordinates and check if the coordinates are duplicate then delete them. 我想查找名称和坐标,并检查坐标是否重复,然后将其删除。 Following is code I have written so far: 以下是我到目前为止编写的代码:

    XmlDocument xmldoc = new XmlDocument();
    XmlReaderSettings settings = new XmlReaderSettings { NameTable = new NameTable() };
    XmlNamespaceManager xmlns = new XmlNamespaceManager(settings.NameTable);
    xmlns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
    XmlParserContext context = new XmlParserContext(null, xmlns, "", XmlSpace.Default);
    XmlReader reader = XmlReader.Create(fileName, settings, context);
    xmldoc.Load(reader);

    // Setup default namespace manager for searching through nodes
    XmlNamespaceManager manager = new XmlNamespaceManager(xmldoc.NameTable);
    string defaultns = xmldoc.DocumentElement.GetNamespaceOfPrefix("");
    manager.AddNamespace("ns", defaultns);

    var values = new HashSet<string>();

    // get a list of all <Placemark> nodes
    XmlNodeList listOfPlacemark = xmldoc.SelectNodes("//ns:Placemark", manager);

    int totalRecordsRemoved = 0;

    // iterate over the <Placemark> nodes
    foreach (XmlNode singlePlaceMark in listOfPlacemark)
    {
        StringBuilder sb = new StringBuilder();
        // Get the name subnode
        XmlNode nameNode = singlePlaceMark.SelectSingleNode("ns:name", manager);

        if (nameNode != null)
        {
            // get a coordinate nodes
            //XmlNodeList coordinatesNode = singlePlaceMark.SelectNodes("//coordinates", manager);

            XmlNode coordinatesNode = singlePlaceMark.SelectSingleNode("ns:coordinates", manager);

            sb.Append(coordinatesNode.InnerXml.ToString());

            if (sb.ToString() != "")
            {
                if (values.Contains(sb.ToString()))
                {                                    
                    singlePlaceMark.RemoveAll();                                    
                }
                else
                {
                    values.Add(sb.ToString());
                }
            }
        }
    }

I am not able to find the coordinate node, how do I get the values in <coordinates> node irrespective of the hierarchy within the placemark node? 我找不到坐标节点,无论地标节点内的层次如何,如何获取<coordinates>节点中的值? Is there a simple way to do this with Linq or any other approach? 是否有使用Linq或任何其他方法执行此操作的简单方法?

EDIT: 编辑:

Two reasons for which I am not using XDocument 我不使用XDocument的两个原因

1. The kml saved after all the processing become of large size because all the nodes get kml as prefix and that is because of "xmlns:kml="http://www.opengis.net/kml/2.2"" tag. 1.在所有处理之后保存的kml变得很大,因为所有节点都将kml作为前缀,这是因为“ xmlns:kml =“ http://www.opengis.net/kml/2.2”“标签。 I don't know how to remove this tag with XDocument so I used XmlDocument which when saved shows xml tags without the prefix. 我不知道如何使用XDocument删除此标签,所以我使用了XmlDocument,该文件在保存时显示不带前缀的xml标签。 2. My kml file does not have " http://www.w3.org/2001/XMLSchema-instance " namespace so when I use XDocument it gives me error - 'xsi' is an undeclared prefix. 2.我的kml文件没有“ http://www.w3.org/2001/XMLSchema-instance ”命名空间,因此当我使用XDocument时,它给我错误-'xsi'是未声明的前缀。 But when I use XmlDocument I don't get this error. 但是当我使用XmlDocument时,不会出现此错误。

Edit 2: 编辑2:

Can you please suggest how can I get the coordinate node at this line XmlNode coordinatesNode = singlePlaceMark.SelectSingleNode("ns:coordinates", manager);? 您能否建议我如何在此行获得坐标节点XmlNodeordinateNode = singlePlaceMark.SelectSingleNode(“ ns:coordinates”,manager);?

A kml file can be huge and cause out of memory exception. kml文件可能很大,并且导致内存不足异常。 I recommend using a XmlReader. 我建议使用XmlReader。 See code below : 参见下面的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);
            reader.MoveToContent();
            while (!reader.EOF)
            {
                if (reader.Name != "Placemark")
                {
                    reader.ReadToFollowing("Placemark");
                }
                if (!reader.EOF)
                {
                    XElement placemark = (XElement)XElement.ReadFrom(reader);
                    Location location = new Location();
                    Location.locations.Add(location);
                    XElement name = placemark.Elements().Where(x => x.Name.LocalName == "name").FirstOrDefault();
                    location.name = (string)name;
                    XElement longitude = placemark.Descendants().Where(x => x.Name.LocalName == "longitude").FirstOrDefault();
                    location.longitude = (int?)longitude;
                    XElement latitude = placemark.Descendants().Where(x => x.Name.LocalName == "latitude").FirstOrDefault();
                    location.latitude = (int?)latitude;
                    XElement altitude = placemark.Descendants().Where(x => x.Name.LocalName == "altitude").FirstOrDefault();
                    location.alt = (int?)altitude;
                    XElement tilt = placemark.Descendants().Where(x => x.Name.LocalName == "tilt").FirstOrDefault();
                    location.tilt = (int?)tilt;
                    XElement heading = placemark.Descendants().Where(x => x.Name.LocalName == "heading").FirstOrDefault();
                    location.heading = (double?)heading;
                    XElement range = placemark.Descendants().Where(x => x.Name.LocalName == "range").FirstOrDefault();
                    location.range = (double?)range;
                    XElement coordinates = placemark.Descendants().Where(x => x.Name.LocalName == "coordinates").FirstOrDefault();
                    if (coordinates != null)
                    {
                        List<string> coordinatesArray = ((string)coordinates).Split(new char[] { ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                        foreach(string coordinate in coordinatesArray)
                        {
                            string[] coordinateArray = coordinate.Split(new char[] { ',' });
                            Coordinate newCoordinate = new Coordinate();
                            if (location.coordinates == null) location.coordinates = new List<Coordinate>();
                            location.coordinates.Add(newCoordinate);

                            newCoordinate.longitude = double.Parse(coordinateArray[0]);
                            newCoordinate.latitude = double.Parse(coordinateArray[1]);
                            newCoordinate.alt = double.Parse(coordinateArray[2]);

                        }

                    }
                }
            }
        }
    }
    public class Location
    {
        public static List<Location> locations = new List<Location>();
        public string name { get; set; }
        public int? longitude { get; set; }
        public int? latitude { get; set; }
        public int? alt { get; set; }
        public int? tilt { get; set; }
        public double? heading { get; set; }
        public double? range { get; set; }
        public List<Coordinate> coordinates { get; set; }
    }
    public class Coordinate
    {
        public double longitude { get; set; }
        public double latitude { get; set; }
        public double alt { get; set; }
    }
}

If you're using XDocument and some LINQ magic the following code removes any other Placemark elements for those instances where the coordinates are duplicate. 如果您使用的是XDocument和LINQ magic,则以下代码将删除坐标重复的那些实例的所有其他Placemark元素。

var xdoc = XDocument.Parse(xml); // XDocument.Load(stream or file);

var kml = (XNamespace) "http://www.opengis.net/kml/2.2";

var coordinates = xdoc
   .Descendants(kml + "Placemark")              // find Placemark elements
   .Where(pm => pm.Element(kml+"name") != null) // which have a name element
   .Descendants(kml + "coordinates")            // find its coordinates 
   .GroupBy(c => c.Value)                       // group by its value
   .Where(c => c.Count() > 1)                   // if we found more then one value
   .Select(c=> c);                              // project the item for removal

int totalRecordsRemoved = 0;
// loop over each key    
foreach(var coord in coordinates)
{
  // loop over the double coordinates, skipping the first item
  foreach(var item in coord.Skip(1))
  {
     totalRecordsRemoved++;
     // remove the Placemark node, 
     // assuming there ALWAYS will be a Placemark up the hierachy
     // if not First will bark
     item.Ancestors(kml + "Placemark").First().Remove();
  }
}

xdoc will now contain your cleaned XML which you can Save or process in any other way you like. 现在, xdoc将包含您清除的XML,您可以按自己喜欢的任何其他方式保存或处理该XML。

XDocument might be hungry for memory so test and verify if its memory consumption fits in your requirements. XDocument可能需要内存,因此请测试并验证其内存消耗是否满足您的要求。

If you need to read a broken XML file with XDocument.Load, use an XMLReader with its own ParserContext and namespacemanager like so: 如果需要使用XDocument.Load读取损坏的XML文件,请使用XMLReader及其自己的ParserContext和namespacemanager,如下所示:

NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
// this adds the missing namespace
nsmgr.AddNamespace("xsi", "https://www.w3.org/2001/XMLSchema-instance");

// Create the XmlParserContext.
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);

// Create the reader. 
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
XmlReader reader = XmlReader.Create(fileOrStream, settings, context);

var xdoc = XDocument.Load(reader);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM