Chris Chedgzoy
Chris Chedgzoy

Reputation: 21

Deserialize a json string when there is no field name.in c#

Normally when I use API's and get a Json string back I simply make a class to suit the string and populate that class using newton JsonConvert.DeserializeObject.

However I now have a Json string which 1 of the fields does not have a name.

{
    "attacks": {
        "114862720": {
            "code": "115dc2b990153c41c33d519b26cc302a",
            "timestamp_started": 1596782220,
            "timestamp_ended": 1596782226,
            "attacker_id": 580816,
            "attacker_name": "chedders",
            "attacker_faction": 32585,
            "attacker_factionname": "Heart of a Pirate",
            "defender_id": 65306,
            "defender_name": "-Clansdancer",
            "defender_faction": 0,
            "defender_factionname": null,
            "result": "Attacked",
            "stealthed": 0,
            "respect_gain": 4.14,
            "chain": 3,
            "modifiers": {
                "fairFight": 3,
                "war": 1,
                "retaliation": 1,
                "groupAttack": 1,
                "overseas": 1,
                "chainBonus": 1
            }
        },
        "114862829": {
            "code": "8bf08c8ceb9b72f05f40235310cd822e",
            "timestamp_started": 1596782339,
            "timestamp_ended": 1596782344,
            "attacker_id": 580816,
            "attacker_name": "chedders",
            "attacker_faction": 32585,
            "attacker_factionname": "Heart of a Pirate",
            "defender_id": 964979,
            "defender_name": "brko21",
            "defender_faction": 0,
            "defender_factionname": null,
            "result": "Attacked",
            "stealthed": 0,
            "respect_gain": 4.11,
            "chain": 4,
            "modifiers": {
                "fairFight": 3,
                "war": 1,
                "retaliation": 1,
                "groupAttack": 1,
                "overseas": 1,
                "chainBonus": 1
            }
        }
    }
}

After attacks is an ID which is unique to each entry.so building a class for this as I normally would just wont work as the ID is unknown.

Any pointers on how to deserialise this string would be most welcome.

Upvotes: 2

Views: 1246

Answers (3)

tmutton
tmutton

Reputation: 1101

Try this json converter.

class AttacksConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Attacks[]);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject attacks = serializer.Deserialize<JObject>(reader);
        Dictionary<string, AttackDetails> result = new Dictionary<string, AttackDetails>();

        foreach (JProperty property in attacks.Properties())
        {
            string attackKey = property.Name;
            Attacks attackValue = property.Value.ToObject<Attacks>();
            
            result.Add(attackKey, new AttackDetails()
            {
                Code = attackValue.Code
            });
        }

        return result;
    }


    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

The full code can be found here: https://github.com/tmutton/StackOverflowQuestion63544325

Upvotes: 1

Kieran Devlin
Kieran Devlin

Reputation: 1433

This would deserialise to a Dictionary<string, Attack>. Where Attack is a type that you define with all of the properties within each object, in the JSON document.

This example assumes you're using NewtonSofts JSON library:

var attacks = JsonConvert.DeserializeObject<Dictionary<string, Attack>>(jsonString);

public class Attack {
    [JsonProperty("code")]
    public string Code { get; set; }

    [JsonProperty("timestamp_started")]
    public long TimestampStarted { get; set; }

    [JsonProperty("timestamp_ended")]
    public long TimestampEnded { get; set; }

    [JsonProperty("attacker_id")]
    public int AttackerId { get; set; }

    [JsonProperty("attacker_name")]
    public string AttackerName { get; set; }

    [JsonProperty("attacker_faction")]
    public int AttackerFaction { get; set; }

    [JsonProperty("attacker_factionname")]
    public string AttackerFactionName { get; set; }

    [JsonProperty("defender_id")]
    public int DefenderId { get; set; }

    [JsonProperty("defender_name")]
    public string DefenderName { get; set; }

    [JsonProperty("defender_faction")]
    public int DefenderFaction { get; set; }

    [JsonProperty("defender_factionname")]
    public string DefenderFactionName { get; set; }

    [JsonProperty("result")]
    public string Result { get; set; }

    [JsonProperty("stealthed")]
    public int Stealthed { get; set; }

    [JsonProperty("respect_gain")]
    public decimal RespectGain { get; set; }

    [JsonProperty("chain")]
    public int Chain { get; set; }

    [JsonProperty("modifiers")]
    public Dictionary<string, int> Modifiers { get; set; }
}

This results in a collection of identifiers against a strongly typed field set.

var allAttacksByFaction = attacks.Where(x => x.Value.AttackerFaction == 1234);
var singleAttack = attacks.Single(x => x.Key == "<value of attack identifier>");

Upvotes: 1

Athanasios Kataras
Athanasios Kataras

Reputation: 26352

You can use the [JsonExtensionData] property.

Here's the official example:

public class DirectoryAccount
{
    // normal deserialization
    public string DisplayName { get; set; }

    // these properties are set in OnDeserialized
    public string UserName { get; set; }
    public string Domain { get; set; }

    [JsonExtensionData]
    private IDictionary<string, JToken> _additionalData;

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        // SAMAccountName is not deserialized to any property
        // and so it is added to the extension data dictionary
        string samAccountName = (string)_additionalData["SAMAccountName"];

        Domain = samAccountName.Split('\\')[0];
        UserName = samAccountName.Split('\\')[1];
    }

    public DirectoryAccount()
    {
        _additionalData = new Dictionary<string, JToken>();
    }
}

So in your OnDeserializing method, you can get the values from the dictionary and add them to a proper field/cast them to a list of objects, etc.

Upvotes: 1

Related Questions