Json Delphi 7 中的图像数组

Json Array of images in Delphi 7

我有一个 Json 这样的:

{  
  "operator": "Capture", 
  "info": {
    "DeviceID": 123456,
    "Listnum": 4,
    "List": [
      {
        "LibSnapID": 1,
        "SnapPicinfo": "data:image/jpeg;base64,Qk2K5wAAAAAAAD......"
      },
      {
        "LibSnapID": 2,
        "SnapPicinfo": "data:image/jpeg;base64,Qk225QAAAAAAADYAAAAoAAAAiAAAAJAAAAA......"
      },
      {
        "LibSnapID": 3,
        "SnapPicinfo": "data:image/jpeg;base64,Qk2K5wAAAAAAADYAAAAoAAAAjAAAAI0AA...."
      },
      {
        "LibSnapID": 4,
        "SnapPicinfo": "data:image/jpeg;base64,Qk3m5QAAAAAAADYAAAAoAAAAjAAAAIwAAAAB.."
      },
    ]
  }
}

我是 Json 的新手,希望能帮助您将 SnapPicinfo 放入 TImage 数组。

这其实很简单,下面是一个完整的示例,以及以下详细信息:

  • 用于解析JSON (or XML) SuperObject by Henri Gourvest does a good job, but no longer supports Delphi 7. Consider using a fork that might seem outdated but is more than enough for what you need。使原始支持 Delphi 7 也很简单。
  • 用于解码 Base64 you can use Daniel Wischnewski's unit - 如果您认为所有的汇编代码都太神秘,您会发现编写自己的 Base64 解码器也不是黑魔法。
  • 我修改了你的原始 JSON 数据,因为它的图片数据明显损坏,现在有 2 个完整的 JPEG(实际上是 JFIF) files, 1 corrupt file and 1 complete GIF 文件 - 这应该证明代码的可靠性以及满足预期的地方,他们失败的地方。
  • 整个代码是一个 DPR 文件 - 按原样保存并(自然地)期望一个控制台应用程序。我不会显示任何 TImage,但查看它们的宽度和高度应该足以证明。
program JsonToImageArray;

{$APPTYPE CONSOLE}

uses
  superobject,  // https://github.com/frostney/superobject
  Base64,  // https://github.com/mwsrc/XtremeRAT/blob/master/Servidor/Passwords/Base64.pas
  ExtCtrls,  // TImage
  Jpeg,  // TJPEGImage
  Classes;  // TStringStream

const
  CRLF= #13#10;  // Windows linebreak

  // Keep in mind that you'll most likely get your JSON data from elsewhere,
  // so don't care if it looks cumbersome here as String constant.
  sJsonInput
  = '{'+ CRLF
  + '  "operator": "Capture",'+ CRLF
  + '  "info": {'+ CRLF
  + '    "DeviceID": 123456,'+ CRLF
  + '    "Listnum": 4,'+ CRLF
  + '    "List": ['+ CRLF

  // A 1x1 JPEG, apparently with the lowest possible filesize.
  // https://gist.github.com/scotthaleen/32f76a413e0dfd4b4d79c2a534d49c0b
  + '      {'+ CRLF
  + '        "LibSnapID": 1,'+ CRLF
  + '        "SnapPicinfo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/'
  + '2wBDAP/////////////////////////////////////////////////////////////////////'
  + '/////////////////wgALCAABAAEBAREA/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPx'
  + 'A="'+ CRLF
  + '      },'+ CRLF

  // Illegal Base64 data and even if converting it partially the data would
  // be way too little to hold a JPEG.
  + '      {'+ CRLF
  + '        "LibSnapID": 2,'+ CRLF
  + '        "SnapPicinfo": "data:image/jpeg;base64,Qk2K5wAAAAAAAD......"'+ CRLF
  + '      },'+ CRLF

  // A 28x26 JPEG, custom-made.
  + '      {'+ CRLF
  + '        "LibSnapID": 3,'+ CRLF
  + '        "SnapPicinfo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/'
  + '2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAw'
  + 'MBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA'
  + 'wMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAAaABwDASIAAhEBAxEB/8QAHwAAAQUBAQEBA'
  + 'QEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1Fh'
  + 'ByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1h'
  + 'ZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxM'
  + 'XGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAA'
  + 'AECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKR'
  + 'obHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2h'
  + 'panN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0t'
  + 'PU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD99tR1G30fT57u7nhtbW1ja'
  + 'aaaZwkcKKMszMeAAASSeABXkH7Mf/BQj4Mftn+OvGnh34U/EHQ/HuofD1rRdcl0fzLixt/tSu0H'
  + 'l3YX7PcBhFJkwSSBShDYPFev6jfppenz3MizNHbxtK4hheaQhRk7UQFmPHCqCSeACa/L3/gkrrm'
  + 'p6J/wV8/bC8Sat4C+MHh/w78adW0W58H6rrfw18QaXY6lHaWl39oaSe4s447XbuUD7S0RdmCpuJ'
  + 'Ap0PfqypvpByXqpRVvmnJ/9uvzCt7tD2q35or5Pd/L9T9SKKKKQBRRRQAUUUUAf//Z"'+ CRLF
  + '      },'+ CRLF

  // A 1x1 GIF as per  but our
  // code just doesn't expect other picture formats than JPEG.
  + '      {'+ CRLF
  + '        "LibSnapID": 2,'+ CRLF
  + '        "SnapPicinfo": "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5B'
  + 'AEAAAAALAAAAAABAAEAAAICRAEAOw=="'+ CRLF
  + '      },'+ CRLF
  + '    ]'+ CRLF
  + '  }'+ CRLF
  + '}';

type
  TArrayImage= Array of TImage;  // So it can be used as parameter


// Actually turning the JSON into TImage instances
function parse
( const sInput: WideString  // JSON String, i.e. '{ "foo": 42 }'
; out aImg: TArrayImage  // All the pictures that we want
): Integer;  // Error reporting: 0 means no error occured
var
  oRoot, oInfo, oList: ISuperObject;  // Walking the data tree from node to node
  vList: TSuperArray;  // The list of pictures is an array, not an object
  iList, iStart: Integer;  // List items and String matches
  sSnapPicinfo, sBase64, sBinary: AnsiString;
  oStr: TStringStream;  // For feeding the JPEG instance with data
  oJpg: TJPEGImage;  // The picture we want
begin
  result:= 0;  // Everything okay so far


  oRoot:= TSuperObject.ParseString( PWideChar(sInput), TRUE );
  if oRoot= nil then begin
    result:= 1;  // JSON parsing failed
    exit;
  end;

  // The whole JSON is one object and we want this member of it
  oInfo:= oRoot.AsObject.O['info'];
  if oInfo= nil then begin
    result:= 2;  // Element "info" not found
    exit;
  end;

  // The whole "info" is one object again and we want this member
  oList:= oInfo.AsObject.O['List'];
  if oList= nil then begin
    result:= 3;  // Element "List" not found
    exit;
  end;

  vList:= oList.AsArray;  // Should be an array
  if vList= nil then begin
    result:= 4;  // List is no array
    exit;
  end;


  // Coming to this position means we successfully walked thru the JSON to at least say
  // how big the array of pictures will be.
  SetLength( aImg, vList.Length );

  for iList:= 0 to vList.Length- 1 do begin
    aImg[iList]:= nil;  // No image yet, many chances of failure ahead

    // Each element of the array is an object again and we want one specific member of it
    sSnapPicinfo:= vList.O[iList].AsObject.S['SnapPicinfo'];

    // Making sure we can deal with the picture format
    iStart:= Pos( 'data:image/jpeg;', sSnapPicinfo );
    if iStart<> 1 then begin
      result:= 5;  // Unsupported picture format
      continue;
    end;

    // Making sure the data really is Base64 encoded
    iStart:= Pos( 'base64,', sSnapPicinfo );
    if iStart= 0 then begin
      result:= 6;  // Unexpected data format
      continue;
    end;


    // All the rest of the text is Base64 data that we'll decode. Should there be an
    // error during the conversion (illegal character, unexpected end...) then the
    // result will always be empty.
    sBase64:= Copy( sSnapPicinfo, iStart+ 7, Length( sSnapPicinfo ) );
    sBinary:= uBase64.Base64Decode( sBase64 );
    if Length( sBinary )= 0 then begin
      result:= 7;  // Base64 conversion failed
      continue;
    end;

    // Now we have the binary of what seems to be a JPEG file
    oStr:= TStringStream.Create( sBinary );
    oJpg:= TJPEGImage.Create;
    try
      try
        // This can fail for various reasons; JPEG comes with a couple of variants
        // and Delphi 7 does not support all of them.
        oJpg.LoadFromStream( oStr );

        // Create instance in the output array
        aImg[iList]:= TImage.Create( nil );
        try
          aImg[iList].AutoSize:= TRUE;  // Otherwise .Height and .Width are no help
          aImg[iList].Picture.Assign( oJpg );  // Again this might fail
        except
          result:= 9;  // Assigning failed
        end;
      except
        result:= 8;  // Image reading failed
      end;
    finally
      oJpg.Free;
    end;
    oStr.Free;  // What should ever fail with a TStringStream?
  end;
end;


var
  aImg: TArrayImage;  // The result we want to have

  // Just print that array of images so we see what it actually contains
  procedure PrintImages( sTitle: String );
  var
    iImg: Integer;
  begin
    Writeln( sTitle, ': ', Length( aImg ), ' images.' );  // How many elements?
    for iImg:= Low( aImg ) to High( aImg ) do begin
      Write( '- Image #', iImg+ 1, ': ' );
      if aImg[iImg]<> nil then begin  // We have an instance and look up the picture dimensions
        Writeln( aImg[iImg].Width, 'x', aImg[iImg].Height );
      end else Writeln( 'empty' );  // No instance
    end;
  end;

begin
  case parse( sJsonInput, aImg ) of  // Handing over the JSON as input and the array as output
    1.. 4: Writeln( 'Failure' );
    5.. 9: PrintImages( 'Partial success' );  // Only 2 out of 4 should be read successfully
    0: PrintImages( 'Full success' );
  end;
end.