ronek22
8/13/2019 - 10:38 PM

Allegro Client -> New Features

Allegro Client -> New Features

Poradnik

Krok pierwszy

Pomoc w kwestii uporania się w powyższym kodzie z poprawnym budowaniem url’a (dodanie jako jej parametru wartości z parametru nraukcji przekazywanego do tej metody)

Nie do końca rozumiem w czym Pan tu potrzebuje pomocy. Primo, jeśli w zapytaniu nie potrzbujemy żadnych Query Params to polecałbym używać po prostu obiektu klasy Uri zamiast UriBuilder. Zamiast bawić się w nieczytelną konkatenacje stringów polecam skorzystać z magii jaką jest String Interpolation

Refactoring

Dodatkowo zrobiłem mały refactoring, w wielu miejscach powtarzany był kod mający na celu wyczyszczenie header'ów i dodanie header'a autoryzacyjnego oraz Accept. Przeniosłem to do metody SetDefaultHeaders(). Również powtarzany był kod odpowiedzialny za wysłanie requesta typu GET i sparsowanie wyników do JSON'a, teraz znajduję się on w funkcji GetRequestAndParseAsync().

Mapowanie obiektu otrzymanego w responsie do klasy w Csharpie

Jeśli zamierzamy często operować na danych otrzymanych w responsach, warto zastanowić nad zmapowaniem ich do odpowiedniej klasy. Dla przykładu napisałem klase Offer, która opowiada za dane na temat oferty w allegro. W nagłówku takiej klasy w której będziemy mapowali otrzymanego jsona musimy dodać dwa nagłówki

[DataContract]
[Serializable]

Klasa ta musi mieć prywatny pusty konstruktor. Teraz możemy zdefiniować jakie pola chcemy zmapować poprzez dodawanie właściwości z nagłowkiem:

[DataMember(Name="nazwa_pola_w_json")]
public type chosen_name {get; set;}

Dla klasy Offer zmapowałem wszystkie nagłówki, poniewaz przy edycji musimy wysłać wszystkie pola. Jednak przy innych klasa nie musi być to konieczne. Możemy napotkać następujący problem niektóre właściwości w otrzymanym jsonie są zagnieżdzone. Dla takich pól mamy dwa wyjścia.

Pierwszy sposób

Możemy użyć typu dynamic, który dynamicznie dostosuje typ do otrzymanych danych. Wtedy jednak dla tej właściwości struktura nie będzie przejrzysta i wygodna do użycia

Przykład

JSON odpowiedzialny za descrition

"description": {
    "sections": [
        {
            "items": [
                {
                    "type": "TEXT",
                    "content": "<p>Nowy XIAOMI REDMI NOTE 5</p><p>Kod PLU: __4949__</p>"
                }
            ]
        }
    ]
}
[DataMember(Name = "description")]
private dynamic _description { get; set; }

[IgnoreDataMember]
public string Description => _description["sections"][0]["items"][0]["content"];

// use access, assume that offer is of type Offer
Console.WriteLine(offer.Description); // Print out text description of Offer

Moim celem było uzyskanie łatwego dostępu do opisu aukcji, a więc zmapowałem Description za pomocą dynamica i ukryłem to w prywatnej właściwości _description. Nastepnie dodałem pole Description z nagłówkiem IgnoreDataMember, ponieważ tego pola nie pobieram z jsona, tylko jest to nie jaki helper.

Drugi sposób

Zamiast używać dynamica mozemy zmapować całego jsona poprzez tworzenie klas, które odpowiadają za kolejne zagnieżdzenia.

Przykład

Otrzymany JSON

"sellingMode": {
    "format": "BUY_NOW",
    "price": {
        "amount": "500.12",
        "currency": "PLN"
    },
    "startingPrice": null,
    "minimalPrice": null
},

Widać, że mamy tu zagnieżdzony klucz price.

startingPrice i minimalPrice również są zagnieżdzone, ale dla nich użyjemy typu dynamic, bo w tej chwili nie są nam potrzebne.

Pola w klasie SellingMode

[DataMember(Name = "format")]
public string Format { get; set; }

[DataMember(Name = "price")]
public Price Price { get; set; }

[DataMember(Name="startingPrice")]
public dynamic StartinPrice { get; set; }

[DataMember(Name="minimalPrice")]
public dynamic MinimalPrice { get; set; }

Tak więc, oprócz klasy SellingMode utworzyliśmy również klasę Price, w której mamy następujące pola:

[DataMember(Name = "amount")]
public string Amount { get; set; }

[DataMember(Name = "currency")]
public string Currency { get; set; }

[IgnoreDataMember]
public decimal Value => decimal.Parse(Amount);

Teraz gdy chcemy uzyskać dostęp do np. ceny:

offer.SellingMode.Price.Amount


Krok drugi - funkcja do edycji aukcji

Pomonicza funkcja do pobranie wszystkich detali na temat podanego numeru aukcji

private async Task<Offer> GetOffer(string nrAukcji)
{
    // Prezetacja String Interpolation
    UriBuilder builder = new UriBuilder($"{API_LINK}sale/offers/{nrAukcji}");
    // Refactoring
    SetDefaultHeaders(); // Ustawienie headerów 
    var json = await GetRequestAndParseAsync(builder.Uri); // Zwraca sparsowany wynik requesta
    return json.ToObject<Offer>(); // Zmapownie sparsowanego jsona do klasy Offer
}

Funkcja odpowiadająca za edycję oferty

public async Task EditOffer(string noAuction, string price, string currency, int available, string unit)
{
    Offer editedOffer = await GetOffer(noAuction); // otrzymanie detali na temat edytowanej aukcji
    // edycja zmapowanego obiektu
    editedOffer.SellingMode.Price.Amount = price; // cena w rest api podana jest jako string
    editedOffer.SellingMode.Price.Currency = currency;
    editedOffer.Stock.Available = available;
    editedOffer.Stock.Unit = unit;

    
    Uri uri = new Uri(API_LINK, $"/sale/offers/{noAuction}");
    SetDefaultHeaders();
    // Json przesyłany jako Payload/Body musimy użyć StringContent, edytowany obiekt serializujemy z powrotem do jsona (dla tego musielismy zmapować wszystkie pola)
    var jsonContent = new StringContent(Utility.Serialize(editedOffer), Encoding.UTF8, "application/vnd.allegro.public.v1+json");

    var response = await _client.PutAsync(uri, jsonContent);
    var contents = await response.Content.ReadAsStringAsync();

    Console.WriteLine("Results");
    Console.WriteLine(JObject.Parse(contents));
}