1

Тема: JSON -> TDataSet

Доброго часу доби!
Цікавить наступне питання: як стандартними засобами Delphi(XE3 і вище) розпарсити JSON-відповідь від сервера і передати її у TDataSet. І чи взагалі, таке існує?

Подякували: Torbins1

2

Re: JSON -> TDataSet

Тут усе досить складно, бо JSON-об'єкт є багатовимірним, а датасет - це двовимірна структура. Хоча звичайно існують вкладені датасети та TDataSetField.
Плюс немає ніяких стандартів на формат JSON-об'єктів, тому одразу ж виникає проблема з парсингом.

  • Найпростіший варіант вирішення цієї проблеми, це ручний парсинг + InMemory-датасет. Для парсингу є стандартні класи у DBXJSON, або сторонні типу SuperObject. У якості датасету у пам'яті можна використати TClientDataSet або більш гнучкий TjvMemoryData.

  • Якщо використання пам'яті є проблемою, то можна спробувати застосувати TAdapterBindSource. Він дозволить парсити JSON-об'єкти динамічно, коли в цьому виникає потреба.

  • Також якісь можливості автоматичної прив'язки до JSON є у DataSnap, але я про них майже нічого не чув.

  • Ну й нарешті REST Library. Ця бібліотека з'явилася щойно у XE5 і призначена для взаємодії з різними веб-сервісами. Вона містить у своєму складі TRESTResponseDataSetAdapter.

Я б радив вам подивитися у першу чергу в сторону REST Library. У XE6 ця бібліотека буде розширена готовими класами для взаємодії з популярними сервісами типу Google Drive.

Подякували: DOP1

3

Re: JSON -> TDataSet

Добавлю:
в Delphi XE2+ є компонент для роботи з JSON, але веб-сервіси часто поршують стандарт (походу на javascript це неважливо).  В мене є правлений модуль, який працює із неправльними JSON-масивами.

можу скинути (якщо ще не загубив його)

4

Re: JSON -> TDataSet

Скидуйте. Буду вдячним  :)

5

Re: JSON -> TDataSet

Здається це)

Просто кинь у папку з проектом і в uses допиши Data.DBXJSON2 замість Data.DBXJSON, а далі працюєш так само як з оригіналом.
(це для Delphi XE2+ походу)


Можливо я ще якісь хелпери писав... не пам'ятаю)

6

Re: JSON -> TDataSet

А де сам файл?

7 Востаннє редагувалося ADR (24.02.2014 14:31:45)

Re: JSON -> TDataSet

То внизу треба було два рази нажати...

class function TJSONObject.ParsePair(const Br: TJSONByteReader; const Parent: TJSONObject): Integer;
var
  Pair: TJSONPair;
  CommaPos: Integer;
begin
  Pair := TJSONPair.Create;
  Parent.AddDescendant(Pair);

//  CommaPos := ParseString(Br, Pair); /////////////////////////////////////////
{ Заміна щоб бачило рядки типу {33:33}
  ConsumeWhitespaces(Br);
  if Br.PeekByte in [Ord('0')..Ord('9')] then
    CommaPos := ParseNumber(Br, Pair)
  else
    CommaPos := ParseString(Br, Pair);
{/}

  if CommaPos > 0 then
  begin
    ConsumeWhitespaces(Br);
    if Br.Empty then
      Exit(-Br.Offset);
    if Br.PeekByte <> Ord(':') then
      Exit(-Br.Offset);
    Br.ConsumeByte;
    ConsumeWhitespaces(Br);
    CommaPos := ParseValue(Br, Pair);
  end;
  Result := CommaPos;
end;
Post's attachments

Data.DBXJSON2.pas 74.48 kb, 461 downloads since 2014-02-24 

8

Re: JSON -> TDataSet

ADR
Мабуть варто це на QC запостити.

9 Востаннє редагувалося ADR (24.02.2014 15:54:16)

Re: JSON -> TDataSet

Torbins написав:

ADR
Мабуть варто це на QC запостити.

Добавив)

http://qc.embarcadero.com/wc/qcmain.aspx?d=122716

(сильно коряво написав по англ?)

10 Востаннє редагувалося ADR (24.02.2014 17:50:45)

Re: JSON -> TDataSet

це хіба не помилка?О_о

procedure TRect.SetWidth(const Value: Integer);
begin
  Self.Right := Self.Left + Value;
end;

тобто якщо задати ширину в одиничку то вона вона буде двійкою (типу 0 і 1)

боюсь вручну міняти, щоб щось не поламати) System.Types системний розділ як не як...

11

Re: JSON -> TDataSet

А хіба Self.Left не від 0 рахується?

12

Re: JSON -> TDataSet

Chemist-i написав:

А хіба Self.Left не від 0 рахується?

в цьому ж і проблема...
коли:
Left := 0
Width := 1
то
Rigth = 1, а має бути нуль.

13

Re: JSON -> TDataSet

ADR написав:
Chemist-i написав:

А хіба Self.Left не від 0 рахується?

в цьому ж і проблема...
коли:
Left := 0
Width := 1
то
Rigth = 1, а має бути нуль.

чому це має бути 0? має бути 1. right показує в координатах праву точку, т.е. ліва плюсь ширина, (ширина теж може бути 0)

14

Re: JSON -> TDataSet

Chemist-i написав:
ADR написав:
Chemist-i написав:

А хіба Self.Left не від 0 рахується?

в цьому ж і проблема...
коли:
Left := 0
Width := 1
то
Rigth = 1, а має бути нуль.

чому це має бути 0? має бути 1. right показує в координатах праву точку, т.е. ліва плюсь ширина, (ширина теж може бути 0)


просто якщо у мене є картинка із Left=0, Rigth=1 то я хотів щоб у Width було 2...

15

Re: JSON -> TDataSet

Ви можете руками додати там де Вам треба. Але коли Left=0, Rigth=1 самі подумайте. ящо зменшити Rigth на одинию, що відбудеться? І Ви хочете сказати, що при Left=0, Rigth=0 ширина буде дорівнювати 1-ці? це не правильно.

16

Re: JSON -> TDataSet

ADR написав:

Добавив)

http://qc.embarcadero.com/wc/qcmain.aspx?d=122716

(сильно коряво написав по англ?)

Варто іще додати які саме сервіси видають json у такому некоректному форматі.

ADR написав:

це хіба не помилка?О_о

procedure TRect.SetWidth(const Value: Integer);
begin
  Self.Right := Self.Left + Value;
end;

тобто якщо задати ширину в одиничку то вона вона буде двійкою (типу 0 і 1)

А існує якийсь стандарт?

17

Re: JSON -> TDataSet

Chemist-i написав:

Ви можете руками додати там де Вам треба. Але коли Left=0, Rigth=1 самі подумайте. ящо зменшити Rigth на одинию, що відбудеться? І Ви хочете сказати, що при Left=0, Rigth=0 ширина буде дорівнювати 1-ці? це не правильно.

але якщо ми маємо картинку шириною в одну точку то ширина має бути 1, а не нуль...

та вручну я вже зробив...


зараз фігньою страдаю. пишу прогу щоб різати скани на яких є декілька фоток розділених білим. роблю рекурсивним поділом картинки на два, доки є прям, білі, лінії. але зараз є проблема із швидкодією)
(ПС я картинку на пряму не ріжу, тільки зберігаю параметри обрізу в TRect)

18

Re: JSON -> TDataSet

Torbins написав:

Варто іще додати які саме сервіси видають json у такому некоректному форматі.

треба фейсбук прошарити. VK їм не цікаво...

Torbins написав:

А існує якийсь стандарт?

навіть не знаю... ну коли я питаю Width у якогось графічного компонента то він мені вертає кількість пікселів...

19 Востаннє редагувалося Chemist-i (24.02.2014 23:03:37)

Re: JSON -> TDataSet

ADR
О, подібним займався нещодавно, отримував доступ до пікселів bmp-шок спочатку

через ScanLine
var
  p1 : pByteArray;
  bmp : TBitMap;
...
//ініціалізація bmp, піксельний формат має бути pf24bit, всякі перетворення із інших форматів, завантаження картинки і т.д;    
...
bmpHeight := bmp.Height;
bmpWidth := bmp.Width;
for j := 0 to bmpHeight-1 do
begin
  p1 := bmp.ScanLine[j];
  for i := 0 to bmpWidth do
  begin
    p1[3*i+0]; //red, byte
    p1[3*i+1]; //green, byte
    p1[3*i+2]; //blue, byte
  end;
end;
А потім написав взагалі функцію доставання з bmp адрес тих пікселів
function setByte(StartAddr:Integer; bWidth, bHeigth, x,y,col:Integer; Value:Byte) : Byte; inline; //d2005 and bigest
begin
  Byte( Pointer( StartAddr +  ((bHeigth-1-Y)*bWidth + X)*3 + (2-Col) )^ ) := Value;  //basic
end;

Виглядає як мєгазбочення, ну тут куча перетворень, щоб вияснити адресу того пікселя (навіть не пікселя, а канала (r,g,b), StartAddr то адреса блока з пікселями, можна отримувати отак

StartAddr := bmp.ScanLine[bmp.height-1];

Але це вже офтоп.

20 Востаннє редагувалося ADR (24.02.2014 23:14:16)

Re: JSON -> TDataSet

Chemist-i написав:

ADR
О, подібним займався нещодавно, отримував доступ до пікселів bmp-шок спочатку

через ScanLine
var
  p1 : pByteArray;
  bmp : TBitMap;
...
//ініціалізація bmp, піксельний формат має бути pf24bit, всякі перетворення із інших форматів, завантаження картинки і т.д;    
...
bmpHeight := bmp.Height;
bmpWidth := bmp.Width;
for j := 0 to bmpHeight-1 do
begin
  p1 := bmp.ScanLine[j];
  for i := 0 to bmpWidth do
  begin
    p1[3*i+0]; //red, byte
    p1[3*i+1]; //green, byte
    p1[3*i+2]; //blue, byte
  end;
end;
А потім написав взагалі функцію доставання з bmp адрес тих пікселів
function setByte(StartAddr:Integer; bWidth, bHeigth, x,y,col:Integer; Value:Byte) : Byte; inline; //d2005 and bigest
begin
  Byte( Pointer( StartAddr +  ((bHeigth-1-Y)*bWidth + X)*3 + (2-Col) )^ ) := Value;  //basic
end;

Виглядає як мєгазбочення, ну тут куча перетворень, щоб вияснити адресу того пікселя (навіть не пікселя, а канала (r,g,b), StartAddr то адреса блока з пікселями, можна отримувати отак

StartAddr := bmp.ScanLine[bmp.height-1];

Але це вже офтоп.

В мене якраз у цьому і була проблема, але я її вирішив до того як прочитав пост(

просто копіюю вміст зображення через TBitmapData.GetPixel в масив, бо GetPixel викликається з бібліотеки, а це дуже довго.

все одно зображення 3500*2500 не піднімає... дам меншу задачу і подивлюсь профілінг (чи як там його звати)


знайшов головну помилку: зробив надто чутливі налаштування і прога поринула у рекурсію (щоб кожну точку окремо взяти))

зараз ніби працює