Как я могу получить узлы XML из этого XML в классическом ASP (MSXML)?

Хорошо, я в своем уме. Кажется, это должно быть совершенно тривиальное дело, но через час я все еще не могу заставить его работать.

Я пытаюсь получить список часовых поясов из Campaign Monitor API; К сожалению, страница, на которой мне нужно это сделать, написана на классическом ASP / Javascript, поэтому я не могу просто использовать оболочку API.

Я делаю такой запрос:

var request = Server.CreateObject("Msxml2.ServerXMLHTTP");

request.open("GET", apiurl + "/User.GetTimezones?ApiKey=" + apikey, false);
request.send();

Правильный XML возвращается с сервера следующим образом:

<anyType d1p1:type="ArrayOfString" xmlns:d1p1="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://api.createsend.com/api/"> 
    <string>(GMT) Casablanca</string> 
    <string>(GMT) Coordinated Universal Time</string> 
    <string>(GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London</string> 
    <string>(GMT) Monrovia, Reykjavik</string> 
    <string>(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna</string> 
    <string>(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague</string> 
    <string>(GMT+01:00) Brussels, Copenhagen, Madrid, Paris</string> 
    (...and so on - I've truncated for the purpose of this question)
</anyType>

Затем я загружаю этот XML в документ MSXML:

var response = Server.CreateObject("Msxml2.DOMDocument.4.0");
response.async = false;
response.validateOnParse = false;
response.resolveExternals = false;

response.setProperty("SelectionNamespaces", "xmlns:d1p1='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://api.createsend.com/api/'");
response.setProperty("SelectionLanguage", "XPath");

if (response.load(request.responseXML)) 
{
    // If I uncomment this, the XML is correctly written out
    // Response.Write(response.xml);

    var nodes = response.selectNodes("//string");

    // No nodes are found, this is always zero
    Response.Write(nodes.length);

    for (var x = 0; x < nodes.length; x++) 
    {
        // Do something with each time zone value here
    }
}

Проблема, как вы можете видеть из комментариев, заключается в том, что я не могу сопоставить эти «строковые» узлы, что бы я ни делал. Я довольно ржавый, когда дело доходит до ASP / Javascript - я подозреваю, что это как-то связано с пространствами имен (я знаю, что у меня были проблемы с этим в прошлом), но я не уверен, что именно.

Кто-нибудь может указать, что я делаю не так? Любая помощь очень ценится!


person Mark Bell    schedule 07.12.2009    source источник


Ответы (2)


Вы не можете переопределить пространство имен по умолчанию, используемое XPath. В MSXML пространство имен XPath по умолчанию всегда является пространством имен «без имени». Однако нет необходимости в том, чтобы набор псевдонимов, используемых в свойстве SelectionNamespaces, соответствовал псевдонимам документа (хотя, конечно, имеет смысл использовать те же самые псевдонимы, когда это возможно).

Определите свой набор пространств имен следующим образом: -

var ns = "xmlns:a='http://api.createsend.com/api/' "
       + "xmlns:d1p1='http://www.w3.org/2001/XMLSchema-instance'"
response.setProperty("SelectionNamespaces", ns);

Теперь вы можете выбрать все string элементы с помощью: -

var nodes = response.selectNodes("//a:string");

Вот как я бы закодировал это в целом: -

var response = Server.CreateObject("MSXML2.DOMDocument.3.0");  // or use 6.0 but not 4.0
response.async = false;
response.validateOnParse = false;
response.resolveExternals = false;


response.setProperty("ServerHTTPRequest", true); 

if (response.load(apiurl + "/User.GetTimezones?ApiKey=" + apikey)) 
{

    var ns = "xmlns:a='http://api.createsend.com/api/' "
           + "xmlns:d1p1='http://www.w3.org/2001/XMLSchema-instance'"
    response.setProperty("SelectionNamespaces", ns);

    response.setProperty("SelectionLanguage", "XPath");  // remove for 4.0 or above is default

    var nodes = response.selectNodes("//a:string");

    Response.Write(nodes.length);

    for (var x = 0; x < nodes.length; x++) 
    {
        // Do something with each time zone value here
    }
}

Примечания:-

  • Для запроса GET нет необходимости использовать отдельный объект ServerXMLHttp, вы можете указать DOMDocument использовать ServerXMLHttp для внутренних целей, включив свойство ServerHTTPRequest. (Кстати, ваш код, похоже, избыточно передает DOMDocument, представленный свойством ResponseXML, в новый DOMDocument).
  • Я предпочитаю использовать версию MSXML 3.0, поскольку она гарантированно присутствует на поддерживаемых платформах. В противном случае я бы установил 6.0 и использовал его.
  • Указание для SelectionLanguage XPath в 4.0 или более поздних версиях является избыточным, это язык выбора по умолчанию.
person AnthonyWJones    schedule 07.12.2009
comment
Похоже, что в моих знаниях XML есть небольшой пробел, лучше прочитать! Все сейчас работают, очень признательны, спасибо. - person Mark Bell; 08.12.2009

Ваше заблуждение заключается в обработке пространства имен по умолчанию. Здесь нет такого понятия, как пространство имен по умолчанию для выражений XPath - вы должны использовать префикс, даже если он не имеет префикса в XML:

var nsDef = "";
nsDef = nsDef + "xmlns:d1p1='http://www.w3.org/2001/XMLSchema-instance' ";
nsDef = nsDef + "xmlns:api='http://api.createsend.com/api/' ";

response.setProperty("SelectionNamespaces", nsDef);
response.setProperty("SelectionLanguage", "XPath");

var nodes = response.selectNodes("//api:string");

Если вы не используете префикс, выражения XPath обрабатываются в пустом пространстве имен. Вот почему вы не можете ничего выбрать с помощью "//string".

person Tomalak    schedule 07.12.2009
comment
Боюсь, что отвечу Энтони, потому что он вышел за рамки служебного долга, но большое спасибо за вашу помощь! - person Mark Bell; 08.12.2009