Skip to main content
Notice removed Authoritative reference needed by CommunityBot
Bounty Ended with no winning answer by CommunityBot
Notice added Authoritative reference needed by Szyszka947
Bounty Started worth 50 reputation by Szyszka947
added 23 characters in body; edited tags
Source Link
Peter Csala
  • 10.8k
  • 1
  • 16
  • 36
[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]
[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]
[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]
[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]
updated code (bug detected), added example result
Source Link
public static Dictionary<string, JObject> ReadAuthorizationDetailsArray(this JArray jArray)
    {
        return jArray
            .Where(p => p is JObject)
            .ToDictionary(k => ((JObject)k).Property("type")!.Value.ToString(), v => (JObject)v);
    }

    public static JArray ExceptAuthorizationDetailArrays(this JArray firstArray, JArray secondArray)
    {
        Func<JValue, string> getJsonValueIdentifier = (JValue jValue)
            => $"{Regex.Replace(jValue.Path, "\\[\\d+\\]", string.Empty)}:{jValue.Value?.ToString()}";

        var firstAuthorizationDetails = firstArray.ReadAuthorizationDetailsArray();
        var secondAuthorizationDetails = secondArray.ReadAuthorizationDetailsArray();

        foreach (var firstAuthorizationDetail in firstAuthorizationDetails)
        {
            if (!secondAuthorizationDetails.ContainsKey(firstAuthorizationDetail.Key))
                continue;

            var secondAuthorizationDetailObject = secondAuthorizationDetails[firstAuthorizationDetail.Key];

            var firstAuthorizationDetailValuesIdentifiers = firstAuthorizationDetail.Value.Descendants()
                .Where(p => p is JValue)
                .ToDictionary(k => getJsonValueIdentifier((JValue)k), v => v);

            var secondAuthorizationDetailValuesIdentifiers = secondAuthorizationDetailObject.Descendants()
                .Where(p => p is JValue)
                .Select(p => getJsonValueIdentifier((JValue)p));

            var exceptedIdentifiers = firstAuthorizationDetailValuesIdentifiers.Keys.Except(secondAuthorizationDetailValuesIdentifiers);
            if (!exceptedIdentifiers.Any())
            {
                firstArray.Remove(firstAuthorizationDetail.Value);
                continue;
            }

            var toRemove = firstAuthorizationDetailValuesIdentifiers.ExceptBy(exceptedIdentifiers, k => k.Key);
.Where(p => p.Key != $".type:{p.Value}");
        foreach (var removable in toRemove.Select(p => p.Value))
            {
                if (removable.Parent is JProperty) removable.Parent.Remove();
                else removable.Remove();
            }

            var emptyDescendants = firstAuthorizationDetail.Value.Descendants().Where(p => (p is JObject || p is JArray) && (p is null || !p.Any())).ToList();
            foreach (var descendant in emptyDescendants)
            {
                if (descendant.Parent is JProperty prop)
                {
                    if (prop.Value is null || ((prop.Value is JObject || prop.Value is JArray) && !prop.Value.Any())) descendant.Parent.Remove();
            }
            else descendant.Remove();
        }
    }

    return firstArray;
}

When firstArray looks as below:

var thirtyJson = @"[
   {
 else descendant    ""type"": ""payment_initiation"",
      ""actions"": [
         ""initiate"",
         ""status"",
         ""cancel""
      ],
      ""locations"": [
         ""https://example.Remove();com/payments""
      ],
      ""instructedAmount"": {
         ""currency"": ""EUR"",
         ""amount"": 123.50
      },
      ""creditorName"": ""Merchant A"",
      ""creditorAccount"": {
         ""iban"": ""DE02100100109307118603""
      },
      ""remittanceInformationUnstructured"": ""Ref Number Merchant""
   },
   {
  return firstArray;   ""type"": ""account_information"",
      ""actions"": [
         ""list_accounts"",
         ""read_balances"",
         ""read_transactions"",
         ""write_accounts""
      ],
      ""locations"": [
         ""https://example.com/accounts""
      ]
   }
]";

And the secondArray looks like it:

var fourtyJson = @"[
   {
      ""type"":""account_information"",
      ""actions"":[
         ""list_accounts"",
         ""read_balances"",
         ""read_transactions""
      ],
      ""locations"":[
         ""https://example.com/accounts""
      ]
   }
]";

The valid result is:

[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]

We can describe this operation like: result = firstArray - secondArray. So result is JSON that contains all firstArray items that are not contained in secondArray. What's more, type property is never removed (unless compared objects with the same type are the equals. Then we remove the entire object from firstArray.)

public static Dictionary<string, JObject> ReadAuthorizationDetailsArray(this JArray jArray)
    {
        return jArray
            .Where(p => p is JObject)
            .ToDictionary(k => ((JObject)k).Property("type")!.Value.ToString(), v => (JObject)v);
    }

    public static JArray ExceptAuthorizationDetailArrays(this JArray firstArray, JArray secondArray)
    {
        Func<JValue, string> getJsonValueIdentifier = (JValue jValue)
            => $"{Regex.Replace(jValue.Path, "\\[\\d+\\]", string.Empty)}:{jValue.Value?.ToString()}";

        var firstAuthorizationDetails = firstArray.ReadAuthorizationDetailsArray();
        var secondAuthorizationDetails = secondArray.ReadAuthorizationDetailsArray();

        foreach (var firstAuthorizationDetail in firstAuthorizationDetails)
        {
            if (!secondAuthorizationDetails.ContainsKey(firstAuthorizationDetail.Key))
                continue;

            var secondAuthorizationDetailObject = secondAuthorizationDetails[firstAuthorizationDetail.Key];

            var firstAuthorizationDetailValuesIdentifiers = firstAuthorizationDetail.Value.Descendants()
                .Where(p => p is JValue)
                .ToDictionary(k => getJsonValueIdentifier((JValue)k), v => v);

            var secondAuthorizationDetailValuesIdentifiers = secondAuthorizationDetailObject.Descendants()
                .Where(p => p is JValue)
                .Select(p => getJsonValueIdentifier((JValue)p));

            var exceptedIdentifiers = firstAuthorizationDetailValuesIdentifiers.Keys.Except(secondAuthorizationDetailValuesIdentifiers);
            if (!exceptedIdentifiers.Any())
            {
                firstArray.Remove(firstAuthorizationDetail.Value);
                continue;
            }

            var toRemove = firstAuthorizationDetailValuesIdentifiers.ExceptBy(exceptedIdentifiers, k => k.Key);
            foreach (var removable in toRemove.Select(p => p.Value))
            {
                if (removable.Parent is JProperty) removable.Parent.Remove();
                else removable.Remove();
            }

            var emptyDescendants = firstAuthorizationDetail.Value.Descendants().Where(p => (p is JObject || p is JArray) && p is null || !p.Any()).ToList();
            foreach (var descendant in emptyDescendants)
            {
                if (descendant.Parent is JProperty prop)
                {
                    if (prop.Value is null || ((prop.Value is JObject || prop.Value is JArray) && !prop.Value.Any())) descendant.Parent.Remove();
                }
                else descendant.Remove();
            }
        }

        return firstArray;
    }
public static Dictionary<string, JObject> ReadAuthorizationDetailsArray(this JArray jArray)
{
    return jArray
        .Where(p => p is JObject)
        .ToDictionary(k => ((JObject)k).Property("type")!.Value.ToString(), v => (JObject)v);
}

public static JArray ExceptAuthorizationDetailArrays(this JArray firstArray, JArray secondArray)
{
    Func<JValue, string> getJsonValueIdentifier = (JValue jValue)
        => $"{Regex.Replace(jValue.Path, "\\[\\d+\\]", string.Empty)}:{jValue.Value?.ToString()}";

    var firstAuthorizationDetails = firstArray.ReadAuthorizationDetailsArray();
    var secondAuthorizationDetails = secondArray.ReadAuthorizationDetailsArray();

    foreach (var firstAuthorizationDetail in firstAuthorizationDetails)
    {
        if (!secondAuthorizationDetails.ContainsKey(firstAuthorizationDetail.Key))
            continue;

        var secondAuthorizationDetailObject = secondAuthorizationDetails[firstAuthorizationDetail.Key];

        var firstAuthorizationDetailValuesIdentifiers = firstAuthorizationDetail.Value.Descendants()
            .Where(p => p is JValue)
            .ToDictionary(k => getJsonValueIdentifier((JValue)k), v => v);

        var secondAuthorizationDetailValuesIdentifiers = secondAuthorizationDetailObject.Descendants()
            .Where(p => p is JValue)
            .Select(p => getJsonValueIdentifier((JValue)p));

        var exceptedIdentifiers = firstAuthorizationDetailValuesIdentifiers.Keys.Except(secondAuthorizationDetailValuesIdentifiers);
        if (!exceptedIdentifiers.Any())
        {
            firstArray.Remove(firstAuthorizationDetail.Value);
            continue;
        }

        var toRemove = firstAuthorizationDetailValuesIdentifiers.ExceptBy(exceptedIdentifiers, k => k.Key).Where(p => p.Key != $".type:{p.Value}");
        foreach (var removable in toRemove.Select(p => p.Value))
        {
            if (removable.Parent is JProperty) removable.Parent.Remove();
            else removable.Remove();
        }

        var emptyDescendants = firstAuthorizationDetail.Value.Descendants().Where(p => (p is JObject || p is JArray) && (p is null || !p.Any())).ToList();
        foreach (var descendant in emptyDescendants)
        {
            if (descendant.Parent is JProperty prop)
            {
                if (prop.Value is null || ((prop.Value is JObject || prop.Value is JArray) && !prop.Value.Any())) descendant.Parent.Remove();
            }
            else descendant.Remove();
        }
    }

    return firstArray;
}

When firstArray looks as below:

var thirtyJson = @"[
   {
      ""type"": ""payment_initiation"",
      ""actions"": [
         ""initiate"",
         ""status"",
         ""cancel""
      ],
      ""locations"": [
         ""https://example.com/payments""
      ],
      ""instructedAmount"": {
         ""currency"": ""EUR"",
         ""amount"": 123.50
      },
      ""creditorName"": ""Merchant A"",
      ""creditorAccount"": {
         ""iban"": ""DE02100100109307118603""
      },
      ""remittanceInformationUnstructured"": ""Ref Number Merchant""
   },
   {
      ""type"": ""account_information"",
      ""actions"": [
         ""list_accounts"",
         ""read_balances"",
         ""read_transactions"",
         ""write_accounts""
      ],
      ""locations"": [
         ""https://example.com/accounts""
      ]
   }
]";

And the secondArray looks like it:

var fourtyJson = @"[
   {
      ""type"":""account_information"",
      ""actions"":[
         ""list_accounts"",
         ""read_balances"",
         ""read_transactions""
      ],
      ""locations"":[
         ""https://example.com/accounts""
      ]
   }
]";

The valid result is:

[
  {
    "type": "payment_initiation",
    "actions": [
      "initiate",
      "status",
      "cancel"
    ],
    "locations": [
      "https://example.com/payments"
    ],
    "instructedAmount": {
      "currency": "EUR",
      "amount": 123.5
    },
    "creditorName": "Merchant A",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformationUnstructured": "Ref Number Merchant"
  },
  {
    "type": "account_information",
    "actions": [
      "write_accounts"
    ]
  }
]

We can describe this operation like: result = firstArray - secondArray. So result is JSON that contains all firstArray items that are not contained in secondArray. What's more, type property is never removed (unless compared objects with the same type are the equals. Then we remove the entire object from firstArray.)

Source Link

Algorithm to compare OAuth2 Rich Authorization Requests

Authorization Requests spec defines new authorization_details parameter. The authorization server have to somehow compare this parameter to decide whether client wants less, more or the same access.

My way to achieve this is to except two JSON arrays, but I feel my algorithm is not clear and not efficient.

My code looks as below:

public static Dictionary<string, JObject> ReadAuthorizationDetailsArray(this JArray jArray)
    {
        return jArray
            .Where(p => p is JObject)
            .ToDictionary(k => ((JObject)k).Property("type")!.Value.ToString(), v => (JObject)v);
    }

    public static JArray ExceptAuthorizationDetailArrays(this JArray firstArray, JArray secondArray)
    {
        Func<JValue, string> getJsonValueIdentifier = (JValue jValue)
            => $"{Regex.Replace(jValue.Path, "\\[\\d+\\]", string.Empty)}:{jValue.Value?.ToString()}";

        var firstAuthorizationDetails = firstArray.ReadAuthorizationDetailsArray();
        var secondAuthorizationDetails = secondArray.ReadAuthorizationDetailsArray();

        foreach (var firstAuthorizationDetail in firstAuthorizationDetails)
        {
            if (!secondAuthorizationDetails.ContainsKey(firstAuthorizationDetail.Key))
                continue;

            var secondAuthorizationDetailObject = secondAuthorizationDetails[firstAuthorizationDetail.Key];

            var firstAuthorizationDetailValuesIdentifiers = firstAuthorizationDetail.Value.Descendants()
                .Where(p => p is JValue)
                .ToDictionary(k => getJsonValueIdentifier((JValue)k), v => v);

            var secondAuthorizationDetailValuesIdentifiers = secondAuthorizationDetailObject.Descendants()
                .Where(p => p is JValue)
                .Select(p => getJsonValueIdentifier((JValue)p));

            var exceptedIdentifiers = firstAuthorizationDetailValuesIdentifiers.Keys.Except(secondAuthorizationDetailValuesIdentifiers);
            if (!exceptedIdentifiers.Any())
            {
                firstArray.Remove(firstAuthorizationDetail.Value);
                continue;
            }

            var toRemove = firstAuthorizationDetailValuesIdentifiers.ExceptBy(exceptedIdentifiers, k => k.Key);
            foreach (var removable in toRemove.Select(p => p.Value))
            {
                if (removable.Parent is JProperty) removable.Parent.Remove();
                else removable.Remove();
            }

            var emptyDescendants = firstAuthorizationDetail.Value.Descendants().Where(p => (p is JObject || p is JArray) && p is null || !p.Any()).ToList();
            foreach (var descendant in emptyDescendants)
            {
                if (descendant.Parent is JProperty prop)
                {
                    if (prop.Value is null || ((prop.Value is JObject || prop.Value is JArray) && !prop.Value.Any())) descendant.Parent.Remove();
                }
                else descendant.Remove();
            }
        }

        return firstArray;
    }

The regex, getting JSON descendants many times, and these nested loops... I feel it's as bad as possible. What shall I refactor in above code to make it very efficient and more readable?