Вызов Restsharp RequestBody, проблема с форматом JSON?

#c# #json #geocoding #restsharp #esri

Вопрос:

TLDR: У меня есть функционирующий скрипт на C#, который вызывает службу геокодирования ESRI. Он передает адреса и возвращает данные LAT/LON. Это работает нормально, пока я встраиваю адреса в URL-адрес. У меня возникли трудности с получением аналогичного кода для работы с содержимым адреса в теле вызова. Цель здесь-позвонить в службу ОТДЫХА с более чем 500 адресами одновременно.

Непосредственно ниже приведен сценарий, который отлично работает, пока я вставляю адреса как часть URL-адреса. Он обрабатывает 2-5 адресов, никаких проблем. Но (вполне предсказуемо) — при попытке увеличить до 100 адресов я получаю сообщение об ошибке «Недопустимый URI: строка Uri слишком длинная».

 ```C# 
SqlDataAdapter geoA = new SqlDataAdapter(SQLStatement, GEOconn);
DataSet GeoDS = new DataSet();
geoA.Fill(GeoDS, "records");
var obj = JObject.FromObject(GeoDS);
obj["records"] = new JArray(obj["records"].Select(jo => new JObject(new JProperty("attributes", jo))));
string geoAJSON = obj.ToString();
geoAJSON = HttpUtility.UrlEncode(geoAJSON);
var con1 = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/geocodeAddresses?f=pjsonamp;token=abc123amp;outFields=ResultID,Status,Score,Addr_type,ShortLabel,City,Subregion,RegionAbbr,Postal,PostalExt,Country,X,Yamp;addresses=";
var client = new RestClient(con1   geoAJSON);
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Cookie", "AGS_ROLES=abc123");
IRestResponse response = client.Execute(request);
ESRIcls esri = JsonConvert.DeserializeObject<ESRIcls>(response.Content);
```
 

Поэтому я пытаюсь использовать тело URL-запроса и перенести все адреса в его тело. Фрагмент сценария, который я использовал до этого момента, приведен ниже (небольшая модификация сверху).

 SqlDataAdapter geoA = new SqlDataAdapter(SQLStatement, GEOconn);
DataSet GeoDS = new DataSet();
geoA.Fill(GeoDS, "records");
var obj = JObject.FromObject(GeoDS);
obj["records"] = new JArray(obj["records"].Select(jo => new JObject(new JProperty("attributes", jo))));
string geoAJSON = obj.ToString();               
geoAJSON = HttpUtility.UrlEncode(geoAJSON);     
var con1 = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/geocodeAddresses?f=pjsonamp;token=abc123amp;outFields=ResultID,Status,Score,Addr_type,ShortLabel,City,Subregion,RegionAbbr,Postal,PostalExt,Country,X,Y";
var client = new RestClient(con1)
client.Timeout = -1
var request = new RestRequest(Method.POST);
request.AddJsonBody(geoAJSON);
//request.AddParameter("application/json", geoAJSON, ParameterType.RequestBody);
request.AddHeader("Cookie", "AGS_ROLES=abc123");
IRestResponse response = client.Execute(request);
ESRIcls esri = JsonConvert.DeserializeObject<ESRIcls>(response.Content);
 

При запуске я получаю следующую ошибку при выполнении этой строки IRestResponse:

код ошибки 400 Расширенный код -2147467259 сообщение: Не удается завершить операцию.

Для справки, вот фрагмент содержимого переменной geoAJSON. Я пробовал несколько вариантов кодировки URL-адресов и не пробовал. Неясно, в какой части этого я ошибся, и был бы признателен за любые предложения.

     "{rn  "records": [rn    {rn      "attributes": {rn        
    "OBJECTID": 144,rn        "Address": "02483 BERRY RD",rn        
    "City": "CEDARTOWN",rn        "Region": "GA",rn        
    "Postal": "30125"rn      }rn    },rn    {rn      
    "attributes": {rn        "OBJECTID": 145,rn        "Address": 
    "0N321 SILVERLEAF BLVD",rn        "City": "WHEATON",rn        
    "Region": "IL",rn        "Postal": "60187-2913"rn      }rn    
    },rn    {rn      "attributes": {rn        "OBJECTID": 146,rn        
    "Address": "1",rn        "City": "ROME",rn        
    "Region": "GA",rn        "Postal": "30165"rn      }rn    
    }rn  ]rn}"
 

Ответ №1:

Для других, столкнувшихся с аналогичной проблемой при геокодировании esri, сработало следующее

 using System;
using System.Data;
using System.Linq;
using System.Data.SqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RestSharp;
using System.Collections.Generic;

namespace GeoCode
{
    class GeoCode_Main
    {
        readonly static string strDDInstance = @"server";       // where is geo.datasources table?
        readonly static string strddDB = "database";
        readonly static string connDDstring = "Data Source="   strDDInstance   ";Initial Catalog="   strddDB   ";Integrated Security=True;MultipleActiveResultSets = True";
        readonly static int intBatchSize = 250;                     // how many rows to send to batch geocoder at one time?
        public static int intRowsProcessed = 0;             // how many geocode addresses have we done this run?

        static void Main()
        {
            Console.WriteLine("Launching GeoCode_Main_BI routine");
            string SQLStatement = "select top "   intBatchSize   " MAFid as OBJECTID, FMCaddr as Address, FMCcity as City, FMCstate as Region, FMCzip as Postal from maf.addresses where GSscore is null "; // and bitwise_nulls = 30 and MAFid < 750";
            string SQLTempRecCount = "select count(*) as RecCnt from maf.addresses_temp";
            using SqlConnection GEOconn = new SqlConnection(connDDstring);
            GEOconn.Open();

            // if maf.addresses_temp has data in it -- some prior run failed -- hit the exit ramp until that gets fixed
            SqlCommand selectCommand = new SqlCommand(SQLTempRecCount, GEOconn);
            int MafTempRecCnt = (int)selectCommand.ExecuteScalar();
            if (MafTempRecCnt > 1)
            {
                Console.WriteLine("Rows exist in maf.addresses_temp and they shouldn't -- bailing out - 1");
                Console.ReadKey();
                return; } // bailout
            do
            {
                // now open up maf.addresses and see if any rows flagged for geocoding. 
                SqlDataAdapter geoDA = new SqlDataAdapter(SQLStatement, GEOconn);
                DataSet GeoDS = new DataSet();
                geoDA.Fill(GeoDS, "records");
                int GeoDSCnt = GeoDS.Tables[0].Rows.Count;
                // likewise - if no recs return -- just exit
                if (GeoDSCnt == 0)
                { 
                    Console.WriteLine("Finished processing records - about to exit");
                    Console.ReadKey();
                    return;
                }
                intRowsProcessed  = GeoDSCnt;
                Console.WriteLine("{0} rows so far", intRowsProcessed);

                var JSONobj = JObject.FromObject(GeoDS);  
                JSONobj["records"] = new JArray(JSONobj["records"].Select(jo => new JObject(new JProperty("attributes", jo))));
                string geoAJSON = JSONobj.ToString();     
                var client = new RestClient("https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/geocodeAddresses");
                client.Timeout = -1;
                var request = new RestRequest(Method.POST);
                request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
                request.AddParameter("addresses", geoAJSON);
                request.AddParameter("forStorage", "true");
                request.AddParameter("outFields", "ResultID,Status,Score,Addr_type,ShortLabel,City,Subregion,RegionAbbr,Postal,PostalExt,Country,X,Y");
                request.AddParameter("f", "pjson");
                request.AddParameter("token", "blahblah");
                request.AddParameter("Cookie", "blahblah");
                IRestResponse response = client.Execute(request);
                ESRIcls esri = JsonConvert.DeserializeObject<ESRIcls>(response.Content);
                int Cntr = 0;
                while (Cntr < esri.locations.Count)
                {
                    //                                Console.WriteLine(esri.locations[Cntr].attributes.Addr_type);
                    var cmd = new SqlCommand
                    {
                        CommandText = "maf.spPopMAF",
                        CommandType = CommandType.StoredProcedure,
                        Connection = GEOconn
                    };
                    cmd.Parameters.Clear();
                    cmd.Parameters.Add("@MAFid", SqlDbType.Int).Value = esri.locations[Cntr].attributes.ResultID;
                    cmd.Parameters.Add("@GSstatus", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.Status;
                    cmd.Parameters.Add("@GSscore", SqlDbType.Decimal).Value = esri.locations[Cntr].attributes.Score;
                    cmd.Parameters.Add("@GSaddresstype", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.Addr_type;
                    cmd.Parameters.Add("@GSaddress", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.ShortLabel;
                    cmd.Parameters.Add("@GScity", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.City;
                    cmd.Parameters.Add("@GScounty", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.Subregion;
                    cmd.Parameters.Add("@GSstate", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.RegionAbbr;
                    cmd.Parameters.Add("@GSzip5", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.Postal;
                    cmd.Parameters.Add("@GSzip4", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.PostalExt;
                    cmd.Parameters.Add("@GSlongitude", SqlDbType.Decimal).Value = esri.locations[Cntr].attributes.X;
                    cmd.Parameters.Add("@GSlatitude", SqlDbType.Decimal).Value = esri.locations[Cntr].attributes.Y;
                    cmd.Parameters.Add("@GScountry", SqlDbType.VarChar).Value = esri.locations[Cntr].attributes.Country;
                    cmd.ExecuteNonQuery();
                    Cntr  ;
                };
                // Console.WriteLine("Just populated esri feed into maf.addresses_temp table");
                // now move content from temp to maf.addresses
                var cmdsp = new SqlCommand
                {
                    CommandText = "maf.spESRIGeoCodedUpToMAFAddresses",
                    CommandType = CommandType.StoredProcedure,
                    Connection = GEOconn
                };
                cmdsp.ExecuteNonQuery();
                // Console.WriteLine("Just executed maf.spESRIGeoCodedUpToMAFAddresses routine");
                SqlCommand scTemp = new SqlCommand(SQLTempRecCount, GEOconn);
                int MafTempRecCnt2 = (int)scTemp.ExecuteScalar();
                if (MafTempRecCnt2 > 0)
                {
                    Console.WriteLine("Rows exist in maf.addresses_temp and they shouldn't -- bailing out - 2 ");
                    Console.ReadKey();
                    return; }
            }
            while (true);
        }
    }


    public class SpatialReference
    {
        public int wkid { get; set; }
        public int latestWkid { get; set; }
    }

    public class Attributes
    {
        public int ResultID { get; set; }
        public string Loc_name { get; set; }
        public string Status { get; set; }
        public Decimal Score { get; set; }
        public string Match_addr { get; set; }
        public string LongLabel { get; set; }
        public string ShortLabel { get; set; }
        public string Addr_type { get; set; }
        public string Type { get; set; }
        public string PlaceName { get; set; }
        public string Place_addr { get; set; }
        public string Phone { get; set; }
        public string URL { get; set; }
        public Decimal Rank { get; set; }
        public string AddBldg { get; set; }
        public string AddNum { get; set; }
        public string AddNumFrom { get; set; }
        public string AddNumTo { get; set; }
        public string AddRange { get; set; }
        public string Side { get; set; }
        public string StPreDir { get; set; }
        public string StPreType { get; set; }
        public string StName { get; set; }
        public string StType { get; set; }
        public string StDir { get; set; }
        public string BldgType { get; set; }
        public string BldgName { get; set; }
        public string LevelType { get; set; }
        public string LevelName { get; set; }
        public string UnitType { get; set; }
        public string UnitName { get; set; }
        public string SubAddr { get; set; }
        public string StAddr { get; set; }
        public string Block { get; set; }
        public string Sector { get; set; }
        public string Nbrhd { get; set; }
        public string District { get; set; }
        public string City { get; set; }
        public string MetroArea { get; set; }
        public string Subregion { get; set; }
        public string Region { get; set; }
        public string RegionAbbr { get; set; }
        public string Territory { get; set; }
        public string Zone { get; set; }
        public string Postal { get; set; }
        public string PostalExt { get; set; }
        public string Country { get; set; }
        public string LangCode { get; set; }
        public int Distance { get; set; }
        public Decimal X { get; set; }
        public Decimal Y { get; set; }
        public Decimal DisplayX { get; set; }
        public Decimal DisplayY { get; set; }
        public Decimal Xmin { get; set; }
        public Decimal Xmax { get; set; }
        public Decimal Ymin { get; set; }
        public Decimal Ymax { get; set; }
        public string ExInfo { get; set; }
    }

    public class Location
    {
        public string address { get; set; }
        public Location location { get; set; }
        public Decimal score { get; set; }
        public Attributes attributes { get; set; }
    }

    public class ESRIcls
    {
        public SpatialReference spatialReference { get; set; }
        public IList<Location> locations { get; set; }
    }
}