简体   繁体   English

使用REST XML Web服务

[英]Consuming a REST XML web service

I'm trying to consume the following web service http://ipinfodb.com/ip_location_api.php this web service returns an xml response, the code below gets the XML response, but somehow when phasing the values from the XML response it does not work. 我正在尝试使用以下Web服务http://ipinfodb.com/ip_location_api.php此Web服务返回一个xml响应,下面的代码获取XML响应,但不知何故,当从XML响应中定相值时,它不会工作。

What is wrong with my code? 我的代码出了什么问题?

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();
    }
}

} }

EDIT: this is the XML response returned 编辑:这是返回的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>

My solution would be: 我的解决方案是:

  • run the xsd.exe utility on your result XML twice to convert it to a XSD (first step) and a C# class (second step) - this would give you a C# class Response 在结果XML上运行xsd.exe实用程序两次以将其转换为XSD(第一步)和C#类(第二步) - 这将为您提供C#类Response

  • next, you can easily deserialize the response into an instance of that class: 接下来,您可以轻松地将响应反序列化为该类的实例:

     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()); 

    and now your result would contain an instance of Response , with all the elements as nice fields in your object. 现在你的result将包含一个Response实例,所有元素都是你对象中的好字段。

Read more about xsd.exe on its MSDN doc page . 在其MSDN文档页面上阅读有关xsd.exe的更多信息。

I use the this same API, I load the response XML into an XDocument and parse eg 我使用相同的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    
}

What you should also do is introduce a custom class eg GeoLocationInfo and wrap your code in a function eg GetGeoLocation(string ip) then instead of writing the info to the console window you can populate & return an instance of that class. 您还应该做的是引入一个自定义类,例如GeoLocationInfo并将您的代码包装在一个函数中,例如GetGeoLocation(string ip)然后您可以填充并返回该类的实例,而不是将信息写入控制台窗口。

You are assuming that first node will be root node but that's not correct. 您假设第一个节点将是根节点,但这不正确。 You will have XmlDeclaration node first and that may get followed by Whitespace nodes. 您将首先拥有XmlDeclaration节点,并且可能会跟随Whitespace节点。 So you should probably structure your code something like 所以你应该把你的代码构造成类似的东西

...
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;
...

But said all that, I would personally prefer to create response XSD (better if web service provides it) and generate classes out of it (using XSD.exe or Xsd2Code ) for serialize/deserialise it. 但是说,我个人更喜欢创建响应XSD(如果Web服务提供它更好)并从中生成类(使用XSD.exe或Xsd2Code )来序列化/反序列化它。

I think you need to use _xtr.MoveToContent(); 我想你需要使用_xtr.MoveToContent(); Method before using the read method.. See if that works 使用read方法之前的方法..看看是否有效

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

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