Modification of the json value in c#. model a correct parsing way

I have written my code where I read an api and get the json output as below.

{"BaseObject":"P|GL",
        "bbl:BL|12|11":"BL|GL|01",
        "kind": "Entity",    
        "BaseObjectDescription":"PlayerBatsman",
        "bbl:B827222":"downBatsman",
        "CreateDate":"06.01.2022",
        "Description":"PlayerBatsman",
 "errors": [
      
    {
            "ErrorMessage": "MissingUnit",
            "ObjType": "Property",
            "Object": "test:batsman",
            "RefObject": "bbl:BATS364"} ],   
        "bbl:NO87872":"GHG66762",    
        "bbl:PONI9912":"bbl:NOI65661",    
        "bbl:KOK1022":"KOK002",
        "bbl:997171SD":"bbl:YUT12232",    
        "bbl:89812":false,    
        "bbl:4894":"pd:96",    
        "bbl:KITE212":"pd:QUTEQ001",
        "ProjectCategory":"Feed",
        "bbf:type":["[\"bbl:P3924\",
        \"boss:Noball\"]"],
        "primarybbf:type":"[\"bbl:P101003924\",\"boss:Tag\"]"}

But now I need to modify my output as something like this :

{
data": {
    "kind": "ENTITY",
    "payload": {
      "tag-batsman:attributes": {
        "bbl:B827222":"downBatsman",
        "bbl:BATS364": null,
        "bbl:NO87872":"GHG66762",
        "bbl:KOK1022":"KOK002",
        "bbl:89812":false       
      },      
      "relations": {
        "bbl:PONI9912":[
          "NOI65661"
        ],
        "bbl:997171SD": [
          "YUT12232"
        ],
        "bbl:4894": [
          "pd:96"
        ],
        "bbl:KITE212": [
          "pd:QUTEQ001"
        ],
        
        "tag-bbf-pubsub:type": [
          "P3924",
          "P101003823",
          "P101003856",
          "P101003858",
          "P101003932",
          "P101004021"
        ]
      },      
      },      
    },
    "type": "EQUIPMENT",
    "version": "data-4-build-606"
  }
}

Basically what I need is to create this json where if "bbl:xyz" key in input json contains bbl: or pd: in their values then they go under "relations" key of output json as shown above and rest all bbl:xyz will go under "attributes" key as shown in above Json model, rest keys and values pair are constant and can be managed. How can I do it in C#?

For starting lets say my input json is recorded as a stream in response message below. How do I proceed from here?

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var req1 = await ProxyRequest(req.Body);
var responseMessage = await req1.Content.ReadAsStreamAsync();

Solution 1:

I'm not sure I fully understood all the requirements and the mapping is not completely clear to me, but you could utilize a library like Newtonsoft.Json to do something like this. I assume this sample should get you on the right track at least.

internal class JsonTranslatorOptions
{
    public JsonTranslatorOptions(
        string kind,
        string type,
        string version)
    {
        Kind = kind;
        Type = type;
        Version = version;
    }

    public string Kind { get; }
    public string Type { get; }
    public string Version { get; }
}

internal enum PropertySection
{
    None,
    Attribute,
    Relation
}

internal class JsonTranslator
{
    private const string KeyKeyword = "bbl:";
    private static readonly string[] ValueKeywords = { KeyKeyword, "pd:" };

    private readonly JObject _attributes = new();
    private readonly JObject _data = new();
    private readonly JObject _payload = new();
    private readonly JObject _relations = new();
    private readonly JObject _root = new();

    public JsonTranslator(JsonTranslatorOptions options)
    {
        Initialize(options);
    }

    public JObject Translate(JObject input)
    {
        TraverseObject(input);

        return _root;
    }

    private void TraverseObject(JObject input)
    {
        foreach (JProperty property in input.Properties())
        {
            TraverseProperty(property);
        }
    }

    private void TraverseArray(JArray array)
    {
        foreach (JToken element in array)
        {
            if (element is JValue)
            {
                continue;
            }

            foreach (JProperty property in ((JObject)element).Properties())
            {
                TraverseProperty(property);
            }
        }
    }

    private void TraverseProperty(JProperty property)
    {
        switch (property.Value.Type)
        {
            case JTokenType.Object:
                TraverseObject((JObject)property.Value);
                break;
            case JTokenType.Array:
                TraverseArray((JArray)property.Value);
                break;
            default:
                MapPropertyValue(property);
                break;
        }
    }

    private void MapPropertyValue(JProperty property)
    {
        switch (GetPropertySection(property))
        {
            case PropertySection.Attribute:
                _attributes.Add(property);
                break;
            case PropertySection.Relation:
                _relations.Add(property);
                break;
        }
    }

    private static PropertySection GetPropertySection(JProperty property)
    {
        var value = property.Value.Value<string?>();

        if (!property.Name.Contains(KeyKeyword))
        {
            return PropertySection.None;
        }

        if (value is null || !ValueKeywords.Any(keyword => value.Contains(keyword)))
        {
            return PropertySection.Attribute;
        }

        return PropertySection.Relation;
    }

    private void Initialize(JsonTranslatorOptions options)
    {
        _root.Add("data", _data);

        InitializeData(options);
    }

    private void InitializeData(JsonTranslatorOptions options)
    {
        _data.Add("kind", options.Kind);
        _data.Add("payload", _payload);
        _data.Add("type", options.Type);
        _data.Add("version", options.Version);

        InitializePayload();
    }

    private void InitializePayload()
    {
        _payload.Add("tag-batsman:attributes", _attributes);
        _payload.Add("relations", _relations);
    }
}

Using it should look like this:

var json = JObject.Parse(@"{""BaseObject"":""P | GL"",""bbl: BL | 12 | 11"":""BL | GL | 01"",""BaseObjectDescription"":""PlayerBatsman"",""bbl: B827222"":""downBatsman"",""CreateDate"":""06.01.2022"",""Description"":""PlayerBatsman"",""errors"":[{""ErrorMessage"":""MissingUnit"",""ObjType"":""Property"",""Object"":""test: batsman"",""RefObject"":""bbl: BATS364""}],""bbl: NO87872"":""GHG66762"",""bbl: PONI9912"":""bbl: NOI65661"",""bbl: KOK1022"":""KOK002"",""bbl: 997171SD"":""bbl: YUT12232"",""bbl: 89812"":false,""bbl: 4894"":""pd: 96"",""bbl: KITE212"":""pd: QUTEQ001"",""ProjectCategory"":""Feed"",""bbf: type"":[""[\""bbl:P3924\"",\""boss:Noball\""]""], ""primarybbf:type"":""[\""bbl:P101003924\"",\""boss:Tag\""]""}");
var options = new JsonTranslatorOptions(
    kind: "ENTITY",
    type: "EQUIPMENT",
    version: "data-4-build-606");
var translator = new JsonTranslator(options);
var output = translator.Translate(json);
Console.WriteLine(output.ToString());

The current output it's giving:

{
  "data": {
    "kind": "ENTITY",
    "payload": {
      "tag-batsman:attributes": {
        "bbl: 89812": false
      },
      "relations": {
        "bbl: PONI9912": "bbl: NOI65661",
        "bbl: 997171SD": "bbl: YUT12232",
        "bbl: 4894": "pd: 96",
        "bbl: KITE212": "pd: QUTEQ001"
      }
    },
    "type": "EQUIPMENT",
    "version": "data-4-build-606"
  }
}