# How to ガイド - XML データ形式

XML データ形式は、以下の SDK のメソッドを使用して指定できます。

メソッド 説明
request_format_xml リクエスト本体を XML 形式に変換します
response_format_xml XML 形式のレスポンス本体を期待します
format_xml リクエストを変換し、XML 形式のレスポンス本体を期待します

# リクエストでの XML ペイロードの作成

カスタムコネクターのアクションは Ruby ハッシュ形式でデータを受信するため、フォーマットメソッドの1つをリクエストの最後につなげることで、これらのハッシュを XML に変換する必要があります。この例では、Intacct Web サービスへのリクエストを XML データ形式で作成する方法を確認します。

Intacct のドキュメント (opens new window)によると、すべてのリクエストは、同じエンドポイント https://api.intacct.com/ia/xml/xmlgw.phtml に POST リクエストとして行う必要があります。ここでは、リクエストは XML データ形式であることが期待されます。

# サンプルコードスニペット

<request>
  <control>
    <senderid>SENDER_ID</senderid>
    <password>PASSWORD</password>
    <controlid>testControlId</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <login>
        <userid>USER_ID</userid>
        <companyid>COMPANY_ID</companyid>
        <password>PASSWORD</password>
      </login>
    </authentication>
    <content>
      <function controlid="testControlId">
        <inspect>
          <object>USERINFO</object>
        </inspect>
      </function>
    </content>
  </operation>
</request>

ここで紹介するのは、GL アカウント情報を取得するサンプルアクション、 Get GL account です。

get_GL_account: {
  input_fields: lambda do
    [
      {
        name: "key",
        label: "Record ID",
        optional: false
      }
    ]
  end,

  execute: lambda do |connection, input|
    response = post("https://api.intacct.com/ia/xml/xmlgw.phtml").
      payload(
        "control": [
          {
            "senderid": [{ "content!": connection["sender_id"] }],
            "password": [{ "content!": connection["sender_password"] }],
            "controlid": [{ "content!": "testControlId" }],
            "uniqueid": [{ "content!": false }],
            "dtdversion": [{ "content!": 3.0 }]
          }
        ],
        "operation": [
          {
            "authentication": [
              {
                "login": [
                  {
                    "userid": [{ "content!": connection["login_username"] }],
                    "companyid": [{ "content!": connection["company_id"] }],
                    "password": [{ "content!": connection["login_password"] }]
                  }
                ]
              }
            ]
            "content": [
              {
                "function": [
                  {
                    "@controlid": "testControlId",
                    "get_list": [
                      {
                      "@object": "glaccount",
                      "@maxitems": "1"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]).
        headers("Content-type": "x-intacct-xml-request").
        format_xml("request").
        dig("response", 0,
            "operation", 0,
            "result", 0,
            "data", 0,
            "glaccount", 0) || {}

    response.inject({}) do |hash (key, value)|
      hash.merge(
        {
          key => value.dig(0, "content!")
        })
    end
  end,

  output_fields: lambda do |object_definitions|
    object_definitions["budget"]
  end
}

これは難解です。分解してみましょう。まず、適切なエンドポイントにリクエストを行う POST を定義します。

post("https://api.intacct.com/ia/xml/xmlgw.phtml")

次に、最初にハッシュ形式でリクエストにペイロードを追加します。

.payload("control": [
         {
           "senderid": [{ "content!": connection["sender_id"] }],
           "password": [{ "content!": connection["sender_password"] }],
           "controlid": [{ "content!": "testControlId" }],
           "uniqueid": [{ "content!": false }],
           "dtdversion": [{ "content!": 3.0 }]
         }
       ],
       "operation": [
         {
           "authentication": [
             {
               "login": [
                 {
                   "userid": [{ "content!": connection["login_username"] }],
                   "companyid": [{ "content!": connection["company_id"] }],
                   "password": [{ "content!": connection["login_password"] }]
                 }
               ]
             }
           ],
           "content": [
             {
               "function": [
                 {
                   "@controlid": "testControlId",
                   "get_list": [
                     {
                       "@object": "glaccount",
                       "@maxitems": "1"
                     }
                   ]
                 }
               ]
             }
           ]
         }
       ])

XML 要素は表記を追加しなくても繰り返すことができるため、要素はハッシュの配列で表されます。要素データは、content! キーと、@ 接頭辞を用いる属性 (例: @object@maxitems) を使用して定義されます。

作成される XML 本体は以下のようになります。

<control>
  <senderid>SENDER_ID</senderid>
  <password>PASSWORD</password>
  <controlid>testControlId</controlid>
  <uniqueid>false</uniqueid>
  <dtdversion>3.0</dtdversion>
</control>
<operation>
  <authentication>
    <login>
      <userid>USER_ID</userid>
      <companyid>COMPANY_ID</companyid>
      <password>PASSWORD</password>
    </login>
  </authentication>
  <content>
    <function controlid="testControlId">
      <get_list object="glaccount" maxitems="1"/>
    </function>
  </content>
</operation>

XML 要素の値は content! キーを使用して定義されています。たとえば、パスワードを <senderid> 要素に割り当てる場合、ハッシュは次のようになります。

{
  "senderid": [{ "content!": connection["sender_id"] }]
}

次に、複雑な XML 要素を作成する方法について説明します。最初に、1度だけ出現する XML 要素 (オブジェクトタイプ) を作成してみましょう。たとえばリクエスト本体の <control> 要素は、多数の要素がネストされている複雑な要素タイプです。作成される XML の一部は以下のようになります。

<control>
  <senderid>SENDER_ID</senderid>
  <password>PASSWORD</password>
  <controlid>testControlId</controlid>
  <uniqueid>false</uniqueid>
  <dtdversion>3.0</dtdversion>
</control>

これを作成するには、相当のハッシュで control ハッシュに直接 content! を定義してはなりません。代わりに、<control> 要素に属する各要素に1つずつ、ネストされたキーを含む単一のハッシュの配列を定義します。

"control": [
  {
    "senderid": [{ "content!": connection["sender_id"] }],
    "password": [{ "content!": connection["sender_password"] }],
    "controlid": [{ "content!": "testControlId" }],
    "uniqueid": [{ "content!": false }],
    "dtdversion": [{ "content!": 3.0 }]
  }
]

<control> 要素の直下にあるすべての要素はプリミティブタイプであることに注意してください。これらの各要素には値のみが含まれます。場合によっては属性が含まれることもあります。

これで、プリミティブ XML 要素の値を定義する方法がわかりました。XML 属性についてはどうでしょうか。XML 本体の <content> 要素を見て、どのように定義されているか確認してみましょう。

<content>
  <function controlid="testControlId">
    <get_list object="glaccount" maxitems="1"/>
  </function>
</content>

この XML 要素では、2つの要素に属性が定義されています。複雑な要素の <function> とプリミティブ要素の <get_list> です。この XML を作成する、相当のハッシュ構造は次のようになります。

"content": [
  {
    "function": [
      {
        "@controlid": "testControlId",
        "get_list": [
          {
            "@object": "glaccount",
            "@maxitems": "1"
          }
        ]
      }
    ]
  }
]

複雑な XML 要素では、属性とネストされた要素が同じ階層で定義されていますが、属性を表す @ 接頭辞が付きます。(<get_list> 要素には "get_list" キー、controlid 属性には "@controlid" キー)

同様に、プリミティブ XML 要素内の要素の値と属性も同じ階層で定義されています。(<get_list> 要素の値には "content!" キー、object 属性には "@object" キー) この例では、<get_list> 要素に値が割り当てられていません。1つを割り当てると、相当のハッシュは次のようになります。

"content": [
  {
    "function": [
      {
        "@controlid": "testControlId",
        "get_list": [
          {
            "@object": "glaccount",
            "@maxitems": "1",
            "content!": input["key"]
          }
        ]
      }
    ]
  }
]

XML に変換されると、次のようになります。

<content>
  <function controlid="testControlId">
    <get_list object="glaccount" maxitems="1">KEY_VALUE<get_list>
  </function>
</content>

次に、簡単なメソッドで必要なヘッダーを追加します。

.headers("Content-type": "x-intacct-xml-request")

最後に format_xml メソッドを使用し (XML でリクエストとレスポンスの両方が必要であるため)、ルート要素名 <request> を引数としてメソッドに渡します。

.format_xml("request")

# レスポンスからの XML の処理

これで、同じリクエストからのレスポンスは次の形式になります。

<response>
  <control>
    <status>success</status>
    <senderid>SENDER_ID</senderid>
    <controlid>testControlId</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <status>success</status>
      <userid>USER_ID</userid>
      <companyid>COMPANY_ID</companyid>
      <sessiontimestamp>2017-03-26T05:24:25-07:00</sessiontimestamp>
    </authentication>
    <result>
      <status>success</status>
      <function>get_list</function>
      <controlid>testControlId</controlid>
      <listtype start="0" end="0" total="107">glaccount</listtype>
      <data>
        <glaccount>
          <recordno>1</recordno>
          <glaccountno>1000</glaccountno>
          <title>Chase Operating Account</title>
          <normalbalance>debit</normalbalance>
          <accounttype>balancesheet</accounttype>
          <closingtype>closed to account</closingtype>
          <closingaccountno/>
          <whenmodified>06/03/2016 20:19:30</whenmodified>
          <status>active</status>
          <requiredept>false</requiredept>
          <requireloc>false</requireloc>
          <taxable>false</taxable>
          <taxcode/>
          <mrccode/>
          <alternativeaccount>None</alternativeaccount>
          <requireproject>false</requireproject>
          <requirecustomer>false</requirecustomer>
          <requirevendor>false</requirevendor>
          <requireemployee>false</requireemployee>
          <requireitem>false</requireitem>
        </glaccount>
      </data>
    </result>
  </operation>
</response>

format_xml がリクエストで呼び出されたため、Workato SDK は相当のハッシュを返します。リクエストと同様に、

  • XML 要素は表記を追加しなくても繰り返すことができるため、要素はハッシュの配列で表されます。
  • 要素属性には、ネストされた要素および要素値と同じ階層で、@ の接頭辞が付いています。
  • 要素値は、"content!" キーの値として渡されます。
{
  "response"=>[
    {
      "control"=>[
        {
          "status"=>[{ "content!"=>"success" }],
          "senderid"=>[{ "content!"=>"SENDER_ID" }],
          "controlid"=>[{ "content!"=>"testControlId" }],
          "uniqueid"=>[{ "content!"=>"false" }],
          "dtdversion"=>[{ "content!"=>"3.0" }]
        }
      ],
      "operation"=>[
        {
          "authentication"=>[
            {
              "status"=>[{ "content!"=>"success" }],
              "userid"=>[{ "content!"=>"USER_ID" }],
              "companyid"=>[{ "content!"=>"COMPANY_ID" }],
              "sessiontimestamp"=>[{ "content!"=>"2017-03-26T08:25:12-07:00" }]
            }
          ],
          "result"=>[
            {
              "status"=>[{ "content!"=>"success" }],
              "function"=>[{ "content!"=>"get_list" }],
              "controlid"=>[{ "content!"=>"testControlId" }],
              "listtype"=>[
                {
                  "@start"=>"0",
                  "@end"=>"0",
                  "@total"=>"107",
                  "content!"=>"glaccount"
                }
              ],
              "data"=>[
                {
                  "glaccount"=>[
                    {
                      "recordno"=>[{ "content!"=>"1" }],
                      "glaccountno"=>[{ "content!"=>"1000" }],
                      "title"=>[{ "content!"=>"Chase Operating Account" }],
                      "normalbalance"=>[{ "content!"=>"debit" }],
                      "accounttype"=>[{ "content!"=>"balancesheet" }],
                      "closingtype"=>[{ "content!"=>"closed to account" }],
                      "closingaccountno"=>[{}],
                      "whenmodified"=>[{ "content!"=>"06/03/2016 20:19:30" }],
                      "status"=>[{ "content!"=>"active" }],
                      "requiredept"=>[{ "content!"=>"false" }],
                      "requireloc"=>[{ "content!"=>"false" }],
                      "taxable"=>[{ "content!"=>"false" }],
                      "taxcode"=>[{}],
                      "mrccode"=>[{}],
                      "alternativeaccount"=>[{ "content!"=>"None" }],
                      "requireproject"=>[{ "content!"=>"false" }],
                      "requirecustomer"=>[{ "content!"=>"false" }],
                      "requirevendor"=>[{ "content!"=>"false" }],
                      "requireemployee"=>[{ "content!"=>"false" }],
                      "requireitem"=>[{ "content!"=>"false" }]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

したがって、dig メソッドを使用して目的のデータを抽出する必要があります。

.dig("response", 0,
     "operation", 0,
     "result", 0,
     "data", 0,
     "glaccount", 0)

これによりハッシュが返されます。

{
  "recordno"=>[{ "content!"=>"1" }],
  "glaccountno"=>[{ "content!"=>"1000" }],
  "title"=>[{ "content!"=>"Chase Operating Account" }],
  "normalbalance"=>[{ "content!"=>"debit" }],
  "accounttype"=>[{ "content!"=>"balancesheet" }],
  "closingtype"=>[{ "content!"=>"closed to account" }],
  "closingaccountno"=>[{}],
  "whenmodified"=>[{ "content!"=>"06/03/2016 20:19:30" }],
  "status"=>[{ "content!"=>"active" }],
  "requiredept"=>[{ "content!"=>"false" }],
  "requireloc"=>[{ "content!"=>"false" }],
  "taxable"=>[{ "content!"=>"false" }],
  "taxcode"=>[{}],
  "mrccode"=>[{}],
  "alternativeaccount"=>[{ "content!"=>"None" }],
  "requireproject"=>[{ "content!"=>"false" }],
  "requirecustomer"=>[{ "content!"=>"false" }],
  "requirevendor"=>[{ "content!"=>"false" }],
  "requireemployee"=>[{ "content!"=>"false" }],
  "requireitem"=>[{ "content!"=>"false" }]
}

最後に、レシピに適した出力スキーマにハッシュを変換します。

response.inject({}) do |hash, (key, value)|
  hash.merge(
    {
      key => value.dig(0, "content!")
    })
end

このアクションの出力は次のようになります。

{
  "recordno": "1",
  "glaccountno": "1000",
  "title": "Chase Operating Account",
  "normalbalance": "debit",
  "accounttype": "balancesheet",
  "closingtype": "closed to account",
  "closingaccountno": null ,
  "whenmodified": "06/03/2016 20:19:30",
  "status": "active",
  "requiredept": "false",
  "requireloc": "false",
  "taxable": "false",
  "taxcode": null ,
  "mrccode": null ,
  "alternativeaccount": "None",
  "requireproject": "false",
  "requirecustomer": "false",
  "requirevendor": "false",
  "requireemployee": "false",
  "requireitem": "false"
}

# アクションの中のアクション例

# リクエスト

POST https://api.intacct.com/ia/xml/xmlgw.phtml
Accept  application/xml
Accept-Encoding gzip, deflate
Content-Type  application/xml
Authorization Bearer
Content-type  x-intacct-xml-request

リクエスト本体:

<request>
  <control>
    <senderid>SENDER_ID</senderid>
    <password>PASSWORD</password>
    <controlid>testControlId</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <login>
        <userid>USER_ID</userid>
        <companyid>COMPANY_ID</companyid>
        <password>PASSWORD</password>
      </login>
    </authentication>
    <content>
      <function controlid="testControlId">
        <get_list object="glaccount" maxitems="1"></get_list>
      </function>
    </content>
  </operation>
</request>

# レスポンス

ステータス: 200 OK

レスポンス本体:

<?xml version="1.0" encoding="UTF-8"?>
<response>
  <control>
    <status>success</status>
    <senderid>SENDER_ID</senderid>
    <controlid>testControlId</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <status>success</status>
      <userid>USER_ID</userid>
      <companyid>COMPANY_ID</companyid>
      <sessiontimestamp>2017-03-26T05:24:25-07:00</sessiontimestamp>
    </authentication>
    <result>
      <status>success</status>
      <function>get_list</function>
      <controlid>testControlId</controlid>
      <listtype start="0" end="0" total="107">glaccount</listtype>
      <data>
        <glaccount>
          <recordno>1</recordno>
          <glaccountno>1000</glaccountno>
          <title>Chase Operating Account</title>
          <normalbalance>debit</normalbalance>
          <accounttype>balancesheet</accounttype>
          <closingtype>closed to account</closingtype>
          <closingaccountno/>
          <whenmodified>06/03/2016 20:19:30</whenmodified>
          <status>active</status>
          <requiredept>false</requiredept>
          <requireloc>false</requireloc>
          <taxable>false</taxable>
          <taxcode/>
          <mrccode/>
          <alternativeaccount>None</alternativeaccount>
          <requireproject>false</requireproject>
          <requirecustomer>false</requirecustomer>
          <requirevendor>false</requirevendor>
          <requireemployee>false</requireemployee>
          <requireitem>false</requireitem>
        </glaccount>
      </data>
    </result>
  </operation>
</response>

抽出され、次のように変換されます。

{
  "recordno": "1",
  "glaccountno": "1000",
  "title": "Chase Operating Account",
  "normalbalance": "debit",
  "accounttype": "balancesheet",
  "closingtype": "closed to account",
  "closingaccountno": null ,
  "whenmodified": "06/03/2016 20:19:30",
  "status": "active",
  "requiredept": "false",
  "requireloc": "false",
  "taxable": "false",
  "taxcode": null ,
  "mrccode": null ,
  "alternativeaccount": "None",
  "requireproject": "false",
  "requirecustomer": "false",
  "requirevendor": "false",
  "requireemployee": "false",
  "requireitem": "false"
}


Last updated: 2023/8/31 1:07:14