Szybki odczyt danych JSON i skopiowanie ich do zmiennych

0

Witam,

Jako że z danymi JSON mam pierwszy raz styczność wykorzystałem gotowe rozwiązanie które oferuje delphi, czyli użyłem typu TJsonTextReader, jednak kod procedury odczytu danych jest bardzo długi ok 800 linijek kodu. Poniżej fragment kodu z tym rozwiązaniem. Proszę was o opinie czy takie rozwiązanie jak stosuję jest dobre? Zastanawiam się czy nie ma jakiegoś szybszego rozwiązania, aby kodu było mniej i szybszy odczyt danych.

if JSON.TokenType = TJsonToken.StartObject then
  begin
    JSON.read;
    while not(JSON.TokenType = TJsonToken.EndObject) do
    begin
      if JSON.TokenType = TJsonToken.PropertyName then
      begin
        if JSON.Value.AsString = 'id' then
        begin
          JSON.read;
          fArrayOfferInfoActive[R].id := JSON.Value.AsString;
        end

        else
           if JSON.Value.AsString = 'parameters' then
              begin
                
                JSON.read; // [
                while not(JSON.TokenType = TJsonToken.EndArray) do
                begin
                  P := Length(fArrayOfferInfoActive[R].parameters);
                  Setlength(fArrayOfferInfoActive[R].parameters, P + 1);

                  JSON.read; // {
                  while not(JSON.TokenType = TJsonToken.EndObject) do
                  begin
                    if JSON.TokenType = TJsonToken.PropertyName then
                      if JSON.Value.AsString = 'id' then
                      begin
                        JSON.read;
                        fArrayOfferInfoActive[R].parameters[P].id := JSON.Value.AsString;
                      end
                      else
                        if JSON.Value.AsString = 'rangeValue' then
                        begin
                          JSON.read;
                          if not(JSON.TokenType = TJsonToken.Null) then
                            while not(JSON.TokenType = TJsonToken.EndObject) do
                            begin
                              if JSON.TokenType = TJsonToken.PropertyName then
                                if JSON.Value.AsString = 'from' then
                                begin
                                  JSON.read;
                                  fArrayOfferInfoActive[R].parameters[P].rangeValue.from := JSON.Value.AsString;
                                end
                                else
                                  if JSON.Value.AsString = 'to' then
                                  begin
                                    JSON.read;
                                    fArrayOfferInfoActive[R].parameters[P].rangeValue.too := JSON.Value.AsString;
                                  end;
                              JSON.read;
                            end
                          else
                          begin
                            fArrayOfferInfoActive[R].parameters[P].rangeValue.from := '';
                            fArrayOfferInfoActive[R].parameters[P].rangeValue.too := '';
                          end
                        end
                        else
                          if JSON.Value.AsString = 'values' then
                          begin
                            
                            Setlength(fArrayOfferInfoActive[R].parameters[P].values, V);
                            JSON.read; // [
                            while not(JSON.TokenType = TJsonToken.EndArray) do
                            begin
                              if JSON.TokenType = TJsonToken.String then
                              begin
                                V := Length(fArrayOfferInfoActive[R].parameters[P].values);
                                Setlength(fArrayOfferInfoActive[R].parameters[P].values, V + 1);
                                fArrayOfferInfoActive[R].parameters[P].values[V] := JSON.Value.AsString;
                              end;
                              JSON.read;
                            end
                          end
                          else
                            if JSON.Value.AsString = 'valuesIds' then
                            begin
                              
                              Setlength(fArrayOfferInfoActive[R].parameters[P].valuesIds, V);
                              JSON.read; // [
                              while not(JSON.TokenType = TJsonToken.EndArray) do
                              begin
                                if JSON.TokenType = TJsonToken.String then
                                begin
                                  V := Length(fArrayOfferInfoActive[R].parameters[P].valuesIds);
                                  Setlength(fArrayOfferInfoActive[R].parameters[P].valuesIds, V + 1);
                                  fArrayOfferInfoActive[R].parameters[P].valuesIds[V] := JSON.Value.AsString;
                                end;
                                JSON.read;
                              end
                            end;

                    JSON.read;
                  end;
                  JSON.read;
                end;

              end else
             if ........ //itd
              end;
    JSON.Read;
end;
1

Niezły brzydal… ;)

Nie znam tej klasy, ale z tego co widzę, można ten kod podzielić na mniejsze procedurki i zwiększyć jego czytelność wykorzystując instrukcję wyboru zamiast drabinki warunków. Najlepiej będzie jeśli napiszesz sobie listę kroków potrzebnych do przeczytania tego pliku, a następnie zastanowisz się nad podziałem kodu na małe procedurki. Dopiero wtedy będzie można usiąść do implementacji.


A tak na marginesie to zrób coś z tym kodem:

V := Length(fArrayOfferInfoActive[R].parameters[P].valuesIds);
Setlength(fArrayOfferInfoActive[R].parameters[P].valuesIds, V + 1);
fArrayOfferInfoActive[R].parameters[P].valuesIds[V] := JSON.Value.AsString;

Nie wiem dlaczego używasz do tego celu zwykłych rekordów zamiast wygodnych list generycznych, ale jeśli już koniecznie musisz ich używać to napisz sobie procedurkę do dodawania ciągów do macierzy. Już nieważne czy jako osobna, globalna procedura czy w postaci helpera – byle nie duplikować tego paskudztwa z SetLength.

0

Przede wszystkim utwórz sobie klasę, która ma przechowywać odczytane dane. Wklej ją (bez i implementacji), wklej przykładowy JSON no i napisz co chcesz odczytać.

0
kAzek napisał(a):

Przede wszystkim utwórz sobie klasę, która ma przechowywać odczytane dane. Wklej ją (bez i implementacji), wklej przykładowy JSON no i napisz co chcesz odczytać.

Zdefiniowałem sobie w klasie tablice typy Array Of TMojTyp i w niej przechowuję dane. Muszę poczytać o typach generycznych jak się je stosuje o czym napisał @furious programming

type
  TMojTyp = record
     id: integer;
     name: string;
     lista: Array Of TMojTyp2
    /// itd.
  end;

Poniżej przykładowy response z serwera allegro z danymi JSON. Jak widać jest tego sporo i muszę zabezpieczyć tak kod że jak pojawi się jakaś nowa zmienna w danych JSON to procedura odczytu będzie działać ewentualnie pominie to pole i wyświetli że znalazła coś nowego

{
  "id":"9807534430",
  "name":"Kolumna Aktywna ",
  "category":
  {
    "id":"122377"
  }
,
  "parameters":
  [
        {
      "id":"11323",
      "valuesIds":
      [
        "11323_238058"
      ]
,
      "values":
      [
              ]
,
      "rangeValue":null
    }
  ]
,
  "ean":null,
  "description":
  {
    "sections":
    [
            {
        "items":
        [
                    {
            "type":"TEXT",
            "content":"opis3"
          }
,
          
          {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/011f63/892aa13429481d29aa7af8491e3"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"TEXT",
            "content":"opis1"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"TEXT",
            "content":"opis"

          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/015ee0/2b3de584dc8a7bc6f9aebd639a2"
          }
,
          
          {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/01a201/204773a448fdcddc373fd994e6c"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/011b89/c560455f34f93ae005812391924"
          }
,
          
          {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/017f59/e08ef554c458119049504e61154"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/01d0ef/de674f684f6f8b73493118a98a40"
          }
,
          
          {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/01e3c9/6125a8534a2196555c8909511ab6"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/011a9d/435040f645c586a5f4d3ac351b30"
          }
,
          
          {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/01adc5/87f93da64ddba9264710c3e58f75"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"IMAGE",
            "url":"https://a.allegroimg.com/original/0157f3/3c68d5bc408295014b52162ee3e3"
          }
        ]
      }
,
      
      {
        "items":
        [
                    {
            "type":"TEXT",
            "content":"<p>[Kod produktu: 2/dd9tRpgOUXAKEdzbhHyh1n+wMOZn6g]</p>"
          }
        ]
      }
    ]
  }
,
  "compatibilityList":null,
  "images":
  [
        {
      "url":"https://a.allegroimg.com/original/011f63/8492aa13429481d9aa7af8491e3"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/015ee0/2b3dee584dc8abc6f9aebd639a2"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/01a201/204773a448fdbcdc373fd994e6c"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/011b89/c560455f434f93a005812391924"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/017f59/e087ef554c45811904504e61154"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/01d0ef/de674f684f6f8b7493118a98a40"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/01e3c9/6125a8534a219655c8909511ab6"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/011a9d/435040f645c5865f4d3ac351b30"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/01adc5/87f93da64ddb9264710c3e58f75"
    }
,
    
    {
      "url":"https://a.allegroimg.com/original/0157f3/3c68d5bc48295014b52162ee3e3"
    }
  ]
,
  "sellingMode":
  {
    "format":"BUY_NOW",
    "price":
    {
      "amount":"349",
      "currency":"PLN"
    }
,
    "startingPrice":null,
    "minimalPrice":null
  }
,
  "stock":
  {
    "available":1,
    "unit":"UNIT"
  }
,
  "publication":
  {
    "duration":"PT240H",
    "status":"ACTIVE",
    "startingAt":null,
    "endingAt":"2019-02-27T17:29:30Z"
  }
,
  "delivery":
  {
    "shippingRates":null,
    "handlingTime":"PT24H",
    "additionalInfo":"",
    "shipmentDate":null
  }
,
  "payments":
  {
    "invoice":"VAT"
  }
,
  "afterSalesServices":
  {
    "impliedWarranty":
    {
      "id":"4e16a7f1-d11a-4a2c-9608-662a6f9db31a"
    }
,
    "returnPolicy":
    {
      "id":"64a569d5-56b4-498d-9ac6-dc68799a3f5f"
    }
,
    "warranty":
    {
      "id":"f73e2d45-a711-4b4d-af93-b1bbbf950c24"
    }
  }
,
  "additionalServices":null,
  "sizeTable":null,
  "promotion":
  {
    "emphasized":false,
    "bold":false,
    "highlight":false,
    "departmentPage":false,
    "emphasizedHighlightBoldPackage":false
  }
,
  "location":
  {
    "countryCode":"PL",
    "province":"WIELKOPOLSKIE",
    "city":"",
    "postCode":""
  }
,
  "external":null,
  "attachments":
  [
      ]
,
  "contact":null,
  "validation":
  {
    "errors":
    [
          ]
,
    "validatedAt":"2019-01-28T12:43:25.923Z"
  }
,
  "createdAt":"2019-01-28T12:43:25Z",
  "updatedAt":"2019-02-17T17:30:03.712Z"
}

1
Rafał D napisał(a):

Zdefiniowałem sobie w klasie tablice typy Array Of TMojTyp i w niej przechowuję dane. Muszę poczytać o typach generycznych jak się je stosuje o czym napisał @furious programming

W skrócie – jeśli potrafisz korzystać z klasy TStringList, to i potrafisz z list generycznych. Bo różnica jest między nimi taka, że inaczej się je deklaruje, a używa tak samo. Tyle że TStringList służy wyłącznie do przechowywania ciągów znaków, a listy generyczne mogą przechowywać cokolwiek (typy proste, rekordy, obiekty itd.) przy jednoczesnym zachowaniu tej samej składni.

Czyli zamiast deklarować kontener w ten sposób:

List: array of TFoo;

deklaruje się go w ten:

List: TList<TFoo>;

Wygoda polega na tym, że uzupełnianie listy i ogólnie manipulowanie jej zawartością wykonuje się za pomocą metod, do tego masz wsparcie enumeratorów i inne bajery, dzięki którym kodu nie trzeba pisać dużo (w odróżnieniu od obsługi macierzy).

Pamiętaj tylko że macierze dynamiczne są zarządzane, a TList jest zwykłą klasą. Musisz najpierw utworzyć instancję za pomocą konstruktora Create, a po użyciu trzeba ją zwolnić za pomocą Free. Czyli dokładnie tak samo jak w przypadku innych klas – nic nowego i nic trudnego.


Jeśli o plik konfiguracyjny chodzi, to jest on dość długi i ma nieprzyjemną, dość nieregularną budowę, więc na pewno będzie trzeba trochę kodu napisać. Na szczęście dane w tym pliku miejscami są zgrupowane w powtarzające się bloki (głównie w pierwszej połowie pliku), dzięki czemu można je czytać w pętlach.

Trudno cokolwiek dokładnie doradzić, bo nie widziałem dokumentacji opisującej zawartość tych konfigów, więc z tym musisz się sam zmierzyć. Sam zrobiłbym to w ten sposób, że dane z każdego węzła (nie wiem jak nazwać te bloki) czytałbym w osobnej metodzie, a każde zagłębienie oznaczałoby wywołanie kolejnych metod o pojedynczej odpowiedzialności. Jeśli węzeł posiada zgrupowane dane o identycznej strukturze (jak np. powtarzające się węzły items czy adresy obrazków w węźle images), to odpowiednie metody wołane byłyby w pętli.

To pozwoliłoby ten kod napisać w postaci krótkich i czytelnych metod, a w razie konieczności, zmiana kolejności odczytywanych danych ograniczać się będzie wyłącznie do zmiany kolejności wywoływania metod lub przenoszenia ich wywołań gdzieś indziej (co zajełoby raptem kilka sekund).

Przy czym cały ten kod ładujący zawartość warto opakować w małą klasę, do czego zachęcam.

2
Rafał D napisał(a):

Poniżej przykładowy response z serwera allegro z danymi JSON. Jak widać jest tego sporo i muszę zabezpieczyć tak kod że jak pojawi się jakaś nowa zmienna w danych JSON to procedura odczytu będzie działać ewentualnie pominie to pole i wyświetli że znalazła coś nowego

Pewnie da się to zrobić w 15 minut...
Tylko trzeba wiedzieć jak, a można np. tak:
https://github.com/PKGeorgiev/Delphi-JsonToDelphiClass

0

W skrócie – jeśli potrafisz korzystać z klasy TStringList, to i potrafisz z list generycznych. Bo różnica jest między nimi taka, że inaczej się je deklaruje, a używa tak samo. Tyle że TStringList służy wyłącznie do przechowywania ciągów znaków, a listy generyczne mogą przechowywać cokolwiek (typy proste, rekordy, obiekty itd.) przy jednoczesnym zachowaniu tej samej składni.
deklaruje się go w ten:

List: TList<TFoo>;

Wygoda polega na tym, że uzupełnianie listy i ogólnie manipulowanie jej zawartością wykonuje się za pomocą metod, do tego masz wsparcie enumeratorów i inne bajery, dzięki którym kodu nie trzeba pisać dużo (w odróżnieniu od obsługi macierzy).

Ale czy to nie będzie wolniejsze bo aby tego użyć musiałbym tak zrobić:

type
  TMojTyp = record
     a: integer;
     b: string;
     c: double
  end;

var
 list: TList<TMojTyp>;

procedure test;
var
  dane: TMojTyp;
begin
  list := TList<TMojTyp>.Create;
  
  dane.a := 0;
  dane.b := 'Test';
  dane.c := 10.10;

  list.add(dane);
  list.free
end;

Zamiast tak:

type
  TMojTyp = record
     a: integer;
     b: string;
     c: double
  end;

var
 list: Array of  TMojTyp;

procedure test;
begin
  Setlength(list,1);
  
  list.a := 0;
  list.b := 'Test';
  list.c := 10.10;

end;

W pierwszym przypadku dane przypisuje do zmiennej typu record a dopiero potem cały rekord dodaje do listy generycznej. W drugim przypadku Od razu zapisuje dane w tabeli.

// Edytowany

Dobra zmęczenie materiału przecież można tak :)

type
  TMojTyp = record
     a: integer;
     b: string;
     c: double
  end;

var
 list: TList<TMojTyp>;

procedure test;
var
  pustedane: TMojTyp;
begin
  list := TList<TMojTyp>.Create;
   
  list.add(pustedane);

  list[0].a := 0;
  list[0].b := 'Test';
  list[0].c := 10.10;

  list.free
end;
0
Rafał D napisał(a):

Ale czy to nie będzie wolniejsze […]

Nie będzie wolniejsze, dlatego że wbudowane klasy list są zoptymalizowane pod kątem alokacji pamięci (pamięć alokowana jest ze sporym zapasem). Ty natomiast co nowa pozycja to powiększasz macierz, a więc wymuszasz relokację bloku pamięci jaki zajmuje, a to jest powolne.

Zresztą, zasugerowałem Ci użycie list generycznych po to, abyś mniej kodu musiał naklepać i mógł go pisać wygodnie. A to że nieco więcej miejsca zajmuje w pamięci instancja takiej listy to niewielka strata w porównaniu do wachlarzu korzyści, jakie ona oferuje.

W pierwszym przypadku dane przypisuje do zmiennej typu record a dopiero potem cały rekord dodaje do listy generycznej. W drugim przypadku od razu zapisuje dane w tabeli.

A weź sobie zadeklaruj funkcję wewnątrz rekordu, która będzie przyjmowała w parametrach dane dla wszystkich pól i zwracała w rezultacie cały rekord – niczego nie będziesz musiał ręcznie uzupełniać, a więc nie będziesz musiał deklarować dodatkowych zminenych.

type
  TFoo = record
    ID: Integer;
    Name: String;
    class function Create(AID: Integer; const AName: String): TFoo; static; inline;
  end;

  class function TFoo.Create(AID: Integer; const AName: String): TFoo;
  begin
    Result.ID := AID;
    Result.Name := AName;
  end;

I teraz będziesz mógł łatwo dodawać strukturki do listy:

var
  List: TList<TFoo>
begin
  List := TList<TFoo>.Create();
  List.Add(TFoo.Create(4, 'name'));
0
list[0].a := 0; 

Otrzymuję błąd: Left side cannot be assigned to

W jaki sposób zmodyfikować tylko jedną zmienną w typie?

2
Rafał D napisał(a):

W jaki sposób zmodyfikować tylko jedną zmienną w typie?

No w ten sposób nie można – musiałbyś pobrać cały rekord do zmiennej, zmienić co potrzebujesz i jego przypisać do danego elementu listy, zastępując go tym zmodyfikowanym.

Olej rekordy, możesz przecież zadeklarować paczuszkę jako prostą klasę z publicznymi polami i w liście przechowywać obiekty – nie będziesz miał problemów z modyfikowaniem danych w taki sposób, w jaki chcesz to robić. Czyli pojedynczą paczuszkę zadeklarować tak:

type
  TFoo = class
    ID: Integer;
    Name: String;
  end;

a listę tak:

var
  List: TObjectList<TFoo>;

Tylko pamiętaj, że klasy nie są zarządzane, więc musisz najpierw tworzyć instancje przykładowego TFoo za pomocą Create. Jeśli chcesz, aby lista zajęła się zwalnianiem pamięci usuwanych obiektów, to w parametrze konstruktora listy przekaż True (jest to domyślna wartość tego parametru, więc jej podanie możesz pominąć).

Przykład:

List := TObjectList<TFoo>.Create();
List.Add(TFoo.Create()); // dorzuca pusty obiekt do listy

List[0].Name := 'foo'; // modyfikuje pole dodanego obiektu

A żebyś nie musiał modyfikować pól w ten sposób, zadeklaruj sobie w klasie TFoo konstruktor, pobierz w nim dane dla pól i je w nim do nich przypisz. Przyda się to przypadku, gdy przed utworzeniem TFoo, dane dla jego pól są znane.

0

@furious programming: Dzięki za rady teraz jest naprawdę cacy :)
Poniżej fragment kodu procedury który zacząłem pisać. Dodatkowo wujek google mi podpowiedział jeszcze jeden typ o nazwie TJSONIterator który bardzo uprasza pracę z plikami Jonson.

procedure TRestApiAllegro.ReadOffers(const JSON: TJsonTextReader);
const
  PrimaryKey: TArrayOfString = ['id', 'name', 'category', 'primaryImage', 'sellingMode', 'saleInfo', 'stock', 'stats', 'publication', 'afterSalesServices',
    'additionalServices', 'external'];
  id = 0;
  name = 1;
  category = 2;
  primaryImage = 3;
  sellingMode = 4;
  saleInfo = 5;
  stock = 6;
  stats = 7;
  publication = 8;
  afterSalesServices = 9;
  additionalServices = 10;
  external_ = 11;

var
  R: Integer;
  Ite: TJSONIterator;

begin
  R := fListOfferInfoActive.Add(TOfferInfo.Create());
  Ite := TJSONIterator.Create(JSON);
  try
    while Ite.Next() do
      case StrToIndex(Ite.Key, @PrimaryKey) of
        id:
          fListOfferInfoActive[R].id := Ite.AsString;
        name:
          fListOfferInfoActive[R].name := Ite.AsString;
        category:
          ;
        primaryImage:
          ;
        sellingMode:
          ;
        saleInfo:
          ;
        stock:
          ;
        stats:
          ;
        publication:
          ;
        afterSalesServices:
          ;
        additionalServices:
          ;
        external_:
          ;
      end;

  finally
    Ite.Free;
  end;
end;

0
Rafał D napisał(a):

@furious programming: Dzięki za rady teraz jest naprawdę cacy :)

Jeśli użycie macierzy nie jest bezwzględnym wymogiem to zawsze korzystaj z list generycznych. Nie dość że wygodniej będzie pracować, to jeszcze nauczysz się nowych rzeczy.

Poniżej fragment kodu procedury który zacząłem pisać. Dodatkowo wujek google mi podpowiedział jeszcze jeden typ o nazwie TJSONIterator który bardzo uprasza pracę z plikami Jonson.

Jeśli masz czas i chęci do pisania tego samemu to pisz – początek wygląda obiecująco (ale te zmienne R i Ite nazwij sensownie, nie bój się dłuższych nazw). Mimo wszystko @wloochacz dał Ci dobrą wskazówkę – cały kod możesz sobie łatwo wygenerować, co zajmie o wiele mniej czasu niż pisanie go ręcznie. Dla innych formatów (np. XML) też są takie generatory – wystarczy poszukać.

1
furious programming napisał(a):

Jeśli masz czas i chęci do pisania tego samemu to pisz – początek wygląda obiecująco (ale te zmienne R i Ite nazwij sensownie, nie bój się dłuższych nazw). Mimo wszystko @wloochacz dał Ci dobrą wskazówkę – cały kod możesz sobie łatwo wygenerować, co zajmie o wiele mniej czasu niż pisanie go ręcznie.

I prawdopodobnie będzie to lepszy kod ;-)

Dla innych formatów (np. XML) też są takie generatory – wystarczy poszukać.

Tak, a zacząłbym te poszukiwania od Delphi IDE, ponieważ XML DataBinding jest obecny w Delphi chyba od wersji Delphi 5?

0
wloochacz napisał(a):

Tak, a zacząłbym te poszukiwania od Delphi IDE, ponieważ XML DataBinding jest obecny w Delphi chyba od wersji Delphi 5?

W zależności od licencji. ;)

0
furious programming napisał(a):
wloochacz napisał(a):

Tak, a zacząłbym te poszukiwania od Delphi IDE, ponieważ XML DataBinding jest obecny w Delphi chyba od wersji Delphi 5?

W zależności od licencji. ;)

Tam się chyba coś zmieniało na przestrzeni lat i teraz jest to również w wersji Pro.
Być może też w Community, ale nie używam, więc nie wiem.

0

@wloochacz: Pobrałem sobie ten generator i nawet fajna sprawa, nie wiem ale wcześniej jakoś tego nie zauważyłem że dałeś linka :( Wygenerowany kod działa nieznacznie wolniej niż ten który napisałem na samym początku. I generalnie wszystko jest oki tylko mam z jednym elementem problem.

Mam taką zmienną ( ** FOffers: TArray<TOffersClass>;** )

TSaleOffersClass = class
private
  FCount: Integer;
 ** FOffers: TArray<TOffersClass>;**
  FTotalCount: Integer;
public
  property count: Integer read FCount write FCount;
  property offers: TArray<TOffersClass> read FOffers write FOffers;
  property totalCount: Integer read FTotalCount write FTotalCount;
  destructor Destroy; override;
  function ToJsonString: string;
  class function FromJsonString(AJsonString: string): TSaleOffersClass;
end;
;

użycie tej funkcji "class function FromJsonString(AJsonString: string): TSaleOffersClass;" bardzo ładnie wczytuje dane ze stringa ale jak wspomniałem wyżej mam problem z FOffers a mianowicie jak dodać do tej listy generycznej kilka nowych pozycji? Albo jak wykonać taką czynność
FOffers := FOffers + FNewOffers

Próbowałem zamiast TArray użyć TList ale wtedy wczytywanie danych ze stringa wywala AV

0
Rafał D napisał(a):

Próbowałem zamiast TArray użyć TList ale wtedy wczytywanie danych ze stringa wywala AV

TArray to tylko wrapper na macierz dynamiczną, która jest zarządzana. Natomiast TList to klasa, której instancję należy najpierw utworzyć, zanim się z niej skorzysta – pisałem Ci wcześniej.

Do złączenia dwóch dynamicznych macierzy jest Concat (choć operator + też to umożliwia).

0

No tak, coś kiepsko u mnie z myśleniem w między czasie napisałem coś takiego:

type
  TAppender<T> = class
    class procedure Append(var Arr: TArray<T>; Value: TArray<T>);
  end;

implementation

class procedure TAppender<T>.Append;
var
  Count: Integer;
begin
  SetLength(Arr, Length(Arr)+Length(Value));
  for Count := 0 to High(Value) do
    Arr[High(Arr)-Count] := Value[Count];
end; 

begin
  TAppender<TOffersClass>.Append(fSaleOffersActive.fOffers,fSaleOffersActive.offers);  //Dodaj nową tablicę
end;
0

A co ze zwykłym dodaniem macierzy za pomocą operatora +? Od XE7 jest to możliwe.

Przy okazji, zamiast pętli możesz skorzystać z Move – szybciej będzie.

0
furious programming napisał(a):

A co ze zwykłym dodaniem macierzy za pomocą operatora +? Od XE7 jest to możliwe.

Używam XE10 Tokio. Takie coś nie działa ze zwykłym +?

var 
  FOffers: TArray<TOffersClass>;
  FOffersNew: TArray<TOffersClass>;
begin
  FOffers := FOffers+FOffersNew;
end;

0

Widać nie działa to dla generycznego TArray. ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1