Использование веб-службы REST XML - PullRequest
11 голосов
/ 14 января 2011

Я пытаюсь использовать следующий веб-сервис http://ipinfodb.com/ip_location_api.php этот веб-сервис возвращает ответ XML, код ниже получает ответ XML, но каким-то образом при поэтапном изменении значений из ответа XML он не работает.

Что не так с моим кодом?

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Net;
using System.Xml;

namespace ConsoleApplication3
{
class Program
{
    static void Main(string[] args)
    {
        HttpWebRequest request = null;
        HttpWebResponse response = null;
        String Xml;

        // Create the web request  
        request = WebRequest.Create("http://api.ipinfodb.com/v2/ip_query.php?key=<yourkey>&ip=74.125.45.100&timezone=true") as HttpWebRequest;

        // Get response  
        using (response = request.GetResponse() as HttpWebResponse)
        {
            // Get the response stream  
            StreamReader reader = new StreamReader(response.GetResponseStream());

            Xml = reader.ReadToEnd();


        }
        // Console xml output  
        Console.WriteLine(Xml); //see if we get the xml response, (YES we do)

        Console.ReadLine();
            string _currentField = "";
            StringReader _sr = new StringReader(Xml);
            XmlTextReader _xtr = new XmlTextReader(_sr);
            _xtr.XmlResolver = null;
            _xtr.WhitespaceHandling = WhitespaceHandling.None;

            // get the root node
            _xtr.Read();

            if ((_xtr.NodeType == XmlNodeType.Element) && (_xtr.Name == "Response"))
            {
                while (_xtr.Read())
                {
                    if ((_xtr.NodeType == XmlNodeType.Element) && (!_xtr.IsEmptyElement))
                    {
                        _currentField = _xtr.Name;
                        _xtr.Read();
                        if (_xtr.NodeType == XmlNodeType.Text)
                        {
                            switch (_currentField)
                            {
                                case "Status":
                                    Console.WriteLine(_xtr.Value); //we print to console for testing purposes, normally assign it to a variable here!
                                    break;
                                case "CountryCode":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "CountryName":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "RegionCode":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "RegionName":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "City":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "ZipPostalCode":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Latitude":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Longitude":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Gmtoffset":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Dstoffset":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "TimezoneName":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Isdst":
                                    Console.WriteLine(_xtr.Value);
                                    break;
                                case "Ip":
                                    Console.WriteLine(_xtr.Value);
                                    break;

                                default:
                                    // unknown field
                                    throw new Exception("Unknown field in response.");
                            }
                        }
                    }
                }
            }
            Console.ReadLine();
    }
}

}

РЕДАКТИРОВАТЬ: это ответ XML возвращается

  <?xml version="1.0" encoding="UTF-8" ?> 
- <Response>
  <Status>OK</Status> 
  <CountryCode>US</CountryCode> 
  <CountryName>United States</CountryName> 
  <RegionCode>06</RegionCode> 
  <RegionName>California</RegionName> 
  <City>Mountain View</City> 
  <ZipPostalCode>94043</ZipPostalCode> 
  <Latitude>37.4192</Latitude> 
  <Longitude>-122.057</Longitude> 
  <Gmtoffset>-28800</Gmtoffset> 
  <Dstoffset>0</Dstoffset> 
  <TimezoneName>America/Los_Angeles</TimezoneName> 
  <Isdst>0</Isdst> 
  <Ip>74.125.45.100</Ip> 
  </Response>

Ответы [ 4 ]

16 голосов
/ 14 января 2011

Мое решение будет следующим:

  • дважды запустите утилиту xsd.exe для вашего XML-результата, чтобы преобразовать его в XSD (первый шаг) и класс C # (второй шаг)это даст вам класс C # Response

  • , затем вы можете легко десериализовать ответ в экземпляр этого класса:

    HttpWebRequest request = WebRequest.Create("http://api.ipinfodb.com/v2/ip_query.php?key=--yourkey--&ip=74.125.45.100&timezone=true") as HttpWebRequest;
    
    XmlSerializer ser = new XmlSerializer(typeof(Response));
    
    WebResponse response = request.GetResponse();
    var result = ser.Deserialize(response.GetResponseStream());
    

    и теперь ваш result будет содержать экземпляр Response со всеми элементами в виде полей в вашем объекте.

Подробнее о xsd.exe на его странице документации MSDN .

14 голосов
/ 14 января 2011

Я использую тот же API, я загружаю XML-ответ в XDocument и анализирую, например,

// build URL up at runtime
string apiKey = ConfigurationManager.AppSettings["geoApiKey"];
string url = String.Format(ConfigurationManager.AppSettings["geoApiUrl"], apiKey, ip);

WebRequest request = WebRequest.Create(url);
try
{
    WebResponse response = request.GetResponse();
    using (var sr = new System.IO.StreamReader(response.GetResponseStream()))
    {
        XDocument xmlDoc = new XDocument();
        try
        {
            xmlDoc = XDocument.Parse(sr.ReadToEnd());
            string status = xmlDoc.Root.Element("Status").Value;
            Console.WriteLine("Response status: {0}", status);
            if (status == "OK")
            { 
                // if the status is OK it's normally safe to assume the required elements
                // are there. However, if you want to be safe you can always check the element
                // exists before retrieving the value
                Console.WriteLine(xmlDoc.Root.Element("CountryCode").Value);
                Console.WriteLine(xmlDoc.Root.Element("CountryName").Value);
                ...
            }                
        }
        catch (Exception)
        {
            // handle if necessary
        }   
    }
}
catch (WebException)
{
    // handle if necessary    
}

Что вы также должны сделать, это ввести собственный класс, например GeoLocationInfoи оберните ваш код в функцию, например, GetGeoLocation(string ip), затем вместо записи информации в окно консоли вы можете заполнить и вернуть экземпляр этого класса.

2 голосов
/ 14 января 2011

Вы предполагаете, что первый узел будет корневым, но это не правильно. Сначала у вас будет XmlDeclaration узел, а за ним могут последовать Whitespace узлы. Так что вы, вероятно, должны структурировать свой код что-то вроде

...
bool isRootRead = false;
while (_xtr.Read())
{
    if (_xtr.NodeType == XmlNodeType.Element)
    {
        if (!isRootRead)
        {
            if (_xter.Name == "Response")
            {
                // root found
                isRootRead = true;
            }
            // jump to next node if root node / ignore other nodes till root element is read
            continue;
        }
        _currentField = _xtr.Name;
        _xtr.Read();
        if (_xtr.NodeType == XmlNodeType.Text)
        {
            switch (_currentField)
            {
                case "Status":
                    Console.WriteLine(_xtr.Value); //we print to console for testing purposes, normally assign it to a variable here!
                    break;
...

Но, несмотря на все это, я лично предпочел бы создать ответный XSD (лучше, если веб-сервис его предоставляет) и генерировать из него классы (используя XSD.exe или Xsd2Code ) для сериализации / десериализации.

0 голосов
/ 14 января 2011

Я думаю, вам нужно использовать _xtr.MoveToContent (); Метод перед использованием метода чтения. Посмотрите, работает ли это

...