This project has moved and is read-only. For the latest updates, please go here.

namespace strictness

Oct 5, 2010 at 5:18 PM

Hello, mattgerg again.

I'm running into some problems loading kml files with the following namespace declaration, which no longer exists: <kml xmlns="http://earth.google.com/kml/2.2">

In the problematic files, replacing the namespace with this resolves the issue: <kml xmlns="http://www.opengis.com/kml/2.2">

Can you think of any clever solution to this problem?  Or is the only solution to modify every kml and kmz file so that they point to the updated namespace?

In code, the issue stems from the Parse(XmlReader reader) function in Parser.cs, where this.GetElement() returns an UnknownElement which cannot be used as the root.  Unfortunately, I have no opportunity to change the namespace programatically beforehand.

Any ideas, or should I start modifying kml files?  

Thanks for your help.

Oct 5, 2010 at 6:39 PM
Edited Oct 5, 2010 at 6:41 PM

I've not had much chance to work with legacy files so the library is based on the OpenGIS specification, hence the strictness on the namespace being http://www.opengis.com/kml/2.2. However, you should be able to use the ParseString method inside the SharpKml.Base.Parser class to ignore the namespaces (not sure how this will work if you have any Atom/GX/XAL elements though).

There should be an example how to use the method in ParseKml.cs in the examples folder, but here's a quick demo showing the other namespace:

using System;
using SharpKml.Base;
using SharpKml.Dom;
using SharpKml.Engine;

class Program
{
    const string Xml =
        "<kml xmlns='http://earth.google.com/kml/2.2'>" +
            "<Placemark>" +
                "<name>SamplePlacemark</name>" +
            "</Placemark>" +
        "</kml>";

    static void Main(string[] args)
    {
        // We'll manually parse the Xml
        Parser parser = new Parser();
        parser.ParseString(Xml, false); // Ignore the namespaces

        // The element will be stored in parser.Root - we'll put it inside
        // a KmlFile for consistency.
        KmlFile file = KmlFile.Create(parser.Root, true);

        // Prove it worked ok
        Kml kml = (Kml)file.Root;
        Placemark placemark = (Placemark)kml.Feature;
        Console.WriteLine(placemark.Name);
    }
}

Oct 8, 2010 at 9:32 PM
Edited Oct 8, 2010 at 9:32 PM

Hello again.  This approach is working well to a degree -- the ParseString() function is able to succssfully parse through the kml.  But I am running into issues at this block during the SerializeElement() routine in Serializer.cs:

 

foreach (var ns in element.Namespaces.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml))
{
        _writer.WriteAttributeString("xmlns", ns.Key, string.Empty, ns.Value);
}

 

When executing the WriteAttributeString() above, an exception is thrown:  "The prefix '' cannot be redefined from 'http://www.opengis.net/kml/2.2' to 'http://earth.google.com/kml/2.2' within the same start element tag."

 

In the block above, ns.Key is blank, but a few lines prior, the following block is already inserting this non-prefixed namespace:  "http://www.opengis.net/kml/2.2"

 

else if (component != null)
{
        _writer.WriteStartElement(component.Name, component.NamespaceUri);
}

 

 

I believe the two namespaces are encroaching upon one another.  Is the intention to preserve the old namespace, insert a new namespace, or to make both namespaces coexist in the same document?  When I comment the offending foreach loop, the program works like a charm.  Any help would be much appreciated!  Let me know if kmz files and code from my end would help out in this matter.

 

Thanks again for your help.  --MattGerg

Oct 9, 2010 at 3:39 PM

Ok, as you guessed the library is trying to preserve the namespaces but tries to use the old and new namespaces when serializing, which isn't a good idea! The problem is should it upgrade the Kml file to the new namespace or preserve the old one? I just tested with libkml what it does in this situation and it just upgrades the namespace:

Input:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
  <Placemark>
    <name>My Placemark</name>
  </Placemark>
</kml>

Output:
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Placemark>
    <name>My Placemark</name>
  </Placemark>
</kml>

I've created a new test case and committed the changes to Parser.cs so you can either download it from the Source Code section or modify the file yourself, as it's a straight forward change. In the ProcessAttributes method of Parser (Parser.cs line 288) you need to add a check to only add a default namespace to UnknownElements:

if (string.Equals("xmlns", _reader.Name, StringComparison.Ordinal)) 
{  
    element.Namespaces.AddNamespace(string.Empty, _reader.Value);
}

Becomes:

if (string.Equals("xmlns", _reader.Name, StringComparison.Ordinal)) 
{  
    if (element is UnknownElement)
    {
        element.Namespaces.AddNamespace(string.Empty, _reader.Value);
    }
}

Thanks for your patience and for your help in improving the library! I think I should add a section to the Documentation on how to load legacy files to help others in this situation.

Oct 13, 2010 at 3:11 PM

This is working perfectly.  Thanks!