0

I'm trying to consume a vendor's api using c# and System.Text.Json. Some API calls allow for flags in the request that add sections to the response. I'm not sure how to handle these (lack of experience).

I can send and receive no problem to standard classes if the format doesn't change. How do I design classes to handle these possible sections?

Following are the classes for a SearchItemsRequest. It contains a SearchItemsRequestSpec that bundles up the search terms. The SearchItemsRequestSpec.ItemType governs the base format of the data returned. The SearchItemsRequest.Flags governs the sections that will be included of that format.

    /// <summary>
    /// Constructor. Sets up for a new search that will return all items.
    /// </summary>
    public SearchItemsRequest()
    {
        Spec = new SearchItemsRequestSpec();
        Force = SearchItemsForce.NewSearch;
        Flags = 1;  // basic response
        From = 0;   // for new search use 0
        To = 0;     // return all elements beginning from index specified in the “from” parameter
    }

    /// <summary>
    /// Search Specification
    /// </summary>
    [JsonPropertyName("spec")]
    public SearchItemsRequestSpec Spec { get; set; }


    /// <summary>
    /// 0 - if such search has been done, then return cached result, 1 - to do a new search
    /// </summary>
    [JsonPropertyName("force")]
    public SearchItemsForce Force { get; set; }


    /// <summary>
    /// Data flags for the response
    /// <para>
    /// The value of this parameter depends on item type; data formats of all item types and their flags are described in the chapter <see href="https://sdk.wialon.com/wiki/en/sidebar/remoteapi/apiref/format/format">Data format</see>
    /// </para>
    /// </summary>
    [JsonPropertyName("flags")]
    public long Flags { get; set; }


    /// <summary>
    /// index of the first returned item (for new search use 0)
    /// </summary>
    [JsonPropertyName("from")]
    public uint From { get; set; }


    /// <summary>
    /// index of the last returned item (if 0 - return all elements beginning from index specified in the “from” parameter)
    /// </summary>
    [JsonPropertyName("to")]
    public uint To { get; set; }
}


/// <summary>
/// Search Criteria
/// <para>
/// <see cref="https://sdk.wialon.com/wiki/en/sidebar/remoteapi/apiref/core/search_items">Search Items API Ref</see>
/// </para>
/// <para>
/// Multi-criteria search is possible<br/>
/// {"itemsType":"avl_unit","propName":"sys_name,rel_user_creator_name","propValueMask":"Volvo*,chdi_test","sortType":"sys_name","propType":"sys_name,rel_user_creator_name","or_logic":0}
/// </para>
/// </summary>
public class SearchItemsRequestSpec
{
    /// <summary>
    /// Constructor.  Defaults are to search for units by unique id
    /// </summary>
    public SearchItemsRequestSpec()
    {
        ItemsType = SearchItemsItemType.avl_unit;
        PropName = SearchItemsPropName.sys_unique_id;
        PropValueMask = string.Empty;
        SortType = SearchItemsPropName.sys_unique_id;
        PropType = SearchItemsPropType.property;
        OrLogic = SearchItemsOrLogic.Disabled;
    }

    [JsonPropertyName("itemsType")]
    public SearchItemsItemType ItemsType { get; set; }

    [JsonPropertyName("propName")]
    public SearchItemsPropName PropName { get; set; }

    [JsonPropertyName("propValueMask")]
    public string PropValueMask { get; set; }

    /// <summary>
    /// Uses the same enum as PropName.  Not a typo
    /// </summary>
    [JsonPropertyName("sortType")]
    public SearchItemsPropName SortType { get; set; }

    [JsonPropertyName("propType")]
    public SearchItemsPropType PropType { get; set; }

    [JsonPropertyName("or_logic")]
    public SearchItemsOrLogic OrLogic { get; set; }
}

When executing the method, I'm currently hardcoded to only support one item type in the response

internal class SearchItemsResponse
{
    [JsonPropertyName("searchSpec")]
    public SearchItemsResponseSpec SearchSpec { get; set; }

    [JsonPropertyName("dataFlags")]
    public uint DataFlags { get; set; }

    [JsonPropertyName("totalItemsCount")]
    public uint TotalItemsCount { get; set; }

    [JsonPropertyName("indexFrom")]
    public uint IndexFrom { get; set; }

    [JsonPropertyName("indexTo")]
    public uint IndexTo { get; set; }

    [JsonPropertyName("items")]
    public List<Item_UnitBasic> Items { get; set; }  //basic data for unit only
}

the possible sections for a unit are

[Flags]
internal enum UnitResponseFlags : long
{
    
    BaseFlag                               = 1,         // 0x00000001
    CustomProperties                       = 2,         // 0x00000002
    BillingProperties                      = 4,         // 0x00000004
    CustomFields                           = 8,         // 0x00000008
    Image                                  = 16,        // 0x00000010
    Messages                               = 32,        // 0x00000020
    GUID                                   = 64,        // 0x00000040
    AdministrativeFields                   = 128,       // 0x00000080
    AdvancedProperties                     = 256,       // 0x00000100
    AvailableForCurrentMomentCommands      = 512,       // 0x00000200
    LastMessageAndPosition                 = 1024,      // 0x00000400
    Sensors                                = 4096,      // 0x00001000
    Counters                               = 8192,      // 0x00002000
    Maintenance                            = 32768,     // 0x00008000
    UnitConfigurationInReports             = 131072,    // 0x00020000 - trip detector and fuel consumption
    ListAllPossibleCommandsForCurrentUnit  = 524288,    // 0x00080000
    MessageParameters                      = 1048576,   // 0x00100000
    UnitConnectionStatus                   = 2097152,   // 0x00200000
    Position                               = 4194304,   // 0x00400000
    ProfileFields                          = 8388608,   // 0x00800000
    SetAllPossibleFlags                    = 4611686018427387903  //  0x3FFFFFFFFFFFFFFF
}

There's a few, and the other item types have their own collections of sections.

If this was basic c#, I'd create an interface and pass that around, but with the deserialization and the possible missing sections, I definitely have a knowledge gap. How do I write a class to handle all the possible permutations of returned data for an item?

Hecatonchires
  • 1,009
  • 4
  • 13
  • 42
  • *"How do I write a class to handle all the possible permutations of returned data for an item"* - are you asking how to define `List` so that it is flexible enough to handle all the data structures? Or are you asking how to check what data flags were returned ? – Zze Mar 07 '23 at 05:26
  • Instead of having a bunch of classes like Item_UnitBasic and massive switch statements, how do I make a flexible Item class? or even a class for each item type that handles the possible sections. – Hecatonchires Mar 07 '23 at 05:48
  • 1
    How about using JsonDocument instead? – Magnus Mar 07 '23 at 07:59
  • @Magnus that looks feasible. https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/use-dom-utf8jsonreader-utf8jsonwriter even says "When you don't have a type to deserialize into. When the JSON you receive doesn't have a fixed schema and must be inspected to know what it contains." – Hecatonchires Mar 07 '23 at 22:26
  • @Magnus make an answer I can accept. I have a followup question, but this one is answered :) – Hecatonchires Mar 08 '23 at 22:17

1 Answers1

0

You can use JsonDocument for json that can not easily be mapped to a static model.
(Don't forget to dispose the document when you are done with it as it uses pooled memory)

Magnus
  • 45,362
  • 8
  • 80
  • 118