将JSON初始化为多个属性

n9vozmp4  于 2023-10-21  发布在  其他
关注(0)|答案(3)|浏览(134)

我正在针对返回JSON数据的第三方API进行编程,但格式可能有点奇怪。某些属性可以是一个对象(包含Id属性),也可以是一个字符串(对象的Id)。例如,以下两种情况均有效:

{
    ChildObject: 'childobjectkey1'
}

{
    ChildObject: {
        Id: 'childobjectkey1',
        // (other properties)
    }
}

我试图使用www.example.com将其转换JSON.net为一个强类型类,但到目前为止还没有太多的运气。我最好的想法是将其序列化为两个属性,一个是字符串,另一个是对象,并为每个属性使用自定义的JsonConverter来允许变量行为:

public abstract class BaseEntity
{
    public string Id { get; set; }
}

public class ChildObject : BaseEntity { }

public class MyObject
{
    [JsonProperty("ChildObject")]
    [JsonConverter(typeof(MyCustomIdConverter))]
    public string ChildObjectId { get; set; }

    [JsonProperty("ChildObject")]
    [JsonConverter(typeof(MyCustomObjectConverter))]
    public ChildObject ChildObject { get; set; }
}

但是,在具有相同PropertyName的两个属性上设置JsonProperty属性会导致异常:
Newtonsoft.Json.JsonSerializationException:“.....”上已存在名为“ChildObject”的成员。使用JsonPropertyAttribute指定另一个名称。
我相当肯定,如果我能克服这个障碍,JsonConverter方法将起作用-我怀疑错误是存在的,因为JsonProperty属性用于序列化和非序列化。在这种情况下,我对序列化这个类没有兴趣--它只会被用作序列化的目标。
我无法控制远程终端(它是第三方API),但我希望能够实现这一点。我不介意它使用我已经开始使用的方法,或者我还没有想到的方法。
This question也是相关的,但没有答案。

lnxxn5zx

lnxxn5zx1#

试试这个(如果你要在代码中使用它,用一些彻底的验证来扩展它):

public class MyObject
{
    public ChildObject MyChildObject;
    public string MyChildObjectId;

    [JsonProperty("ChildObject")]
    public object ChildObject
    {
        get
        {
            return MyChildObject;
        }
        set
        {
            if (value is JObject)
            {
                MyChildObject = ((JToken)value).ToObject<ChildObject>();
                MyChildObjectId = MyChildObject.Id;
            }
            else
            {
                MyChildObjectId = value.ToString();
                MyChildObject = null;
            }
        }
    }
}
eh57zj3b

eh57zj3b2#

与其为每个字段创建两个单独的转换器,不如为“main”属性创建一个转换器,并将另一个转换器链接到它。ChildObjectId是从ChildObject导出的。

public class MyObject
{
    [JsonIgnore]
    public string ChildObjectId
    {
        get { return ChildObject.Id; }

        // I would advise against having a setter here
        // you should only allow changes through the object only
        set { ChildObject.Id = value; }
    }

    [JsonConverter(typeof(MyObjectChildObjectConverter))]
    public ChildObject ChildObject { get; set; }
}

现在转换ChildObject可能是一个挑战。对象有两种可能的表示形式:字符串或对象。确定您拥有的表示形式并执行转换。

public class MyObjectChildObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ChildObject);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = serializer.Deserialize<JToken>(reader);
        switch (obj.Type)
        {
        case JTokenType.Object:
            return ReadAsObject(obj as JObject);
        case JTokenType.String:
            return ReadAsString((string)(JValue)obj);
        default:
            throw new JsonSerializationException("Unexpected token type");
        }
    }

    private object ReadAsObject(JObject obj)
    {
        return obj.ToObject<ChildObject>();
    }

    private object ReadAsString(string str)
    {
        // do a lookup for the actual object or whatever here
        return new ChildObject
        {
            Id = str,
        };
    }
}
ogsagwnx

ogsagwnx3#

在这种情况下我会这么做。

  • 在父类中,子对象只有一个属性,并使其类型为ChildObject
  • 创建一个自定义的JsonConverter,它可以检查JSON,并且:
  • 如果数据存在,则将子对象的完整示例示例化,或者
  • 创建子对象的新示例并设置其ID,将所有其他属性保留为空。(或者你可以像Jeff Mercado建议的那样,让转换器根据ID从数据库加载对象,如果这适用于你的情况的话。
  • 或者,在子对象上放置一个属性,指示它是否完全填充。转换器可以在格式化期间设置此属性。

在示例化之后,如果JSON中有一个ChildObject属性(带有ID或完整的对象值),则可以保证有一个ChildObject示例,并且可以从中获取其ID;否则,如果JSON中没有ChildObject属性,则父类中的ChildObject属性将为null。
下面是一个完整的工作示例来演示。在本例中,我修改了父类,使其包含ChildObject的三个独立示例,以显示JSON中的不同可能性(仅字符串ID、完整对象和不存在)。它们都使用相同的转换器。我还向ChildObject类添加了一个Name属性和一个IsFullyPopulated属性。
以下是DTO类:

public abstract class BaseEntity
{
    public string Id { get; set; }
}

public class ChildObject : BaseEntity 
{
    public string Name { get; set; }
    public bool IsFullyPopulated { get; set; }
}

public class MyObject
{
    [JsonProperty("ChildObject1")]
    [JsonConverter(typeof(MyCustomObjectConverter))]
    public ChildObject ChildObject1 { get; set; }

    [JsonProperty("ChildObject2")]
    [JsonConverter(typeof(MyCustomObjectConverter))]
    public ChildObject ChildObject2 { get; set; }

    [JsonProperty("ChildObject3")]
    [JsonConverter(typeof(MyCustomObjectConverter))]
    public ChildObject ChildObject3 { get; set; }
}

下面是转换器:

class MyCustomObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(ChildObject));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        ChildObject child = null;
        if (token.Type == JTokenType.String)
        {
            child = new ChildObject();
            child.Id = token.ToString();
            child.IsFullyPopulated = false;
        }
        else if (token.Type == JTokenType.Object)
        {
            child = token.ToObject<ChildObject>();
            child.IsFullyPopulated = true;
        }
        else if (token.Type != JTokenType.Null)
        {
            throw new JsonSerializationException("Unexpected token: " + token.Type);
        }
        return child;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

下面是演示转换器操作的测试程序:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        {
            ""ChildObject1"": 
            {
                ""Id"": ""key1"",
                ""Name"": ""Foo Bar Baz""
            },
            ""ChildObject2"": ""key2""
        }";

        MyObject obj = JsonConvert.DeserializeObject<MyObject>(json);

        DumpChildObject("ChildObject1", obj.ChildObject1);
        DumpChildObject("ChildObject2", obj.ChildObject2);
        DumpChildObject("ChildObject3", obj.ChildObject3);
    }

    static void DumpChildObject(string prop, ChildObject obj)
    {
        Console.WriteLine(prop);
        if (obj != null)
        {
            Console.WriteLine("   Id: " + obj.Id);
            Console.WriteLine("   Name: " + obj.Name);
            Console.WriteLine("   IsFullyPopulated: " + obj.IsFullyPopulated);
        }
        else
        {
            Console.WriteLine("   (null)");
        }
        Console.WriteLine();
    }
}

下面是上面的输出:

ChildObject1
   Id: key1
   Name: Foo Bar Baz
   IsFullyPopulated: True

ChildObject2
   Id: key2
   Name:
   IsFullyPopulated: False

ChildObject3
   (null)

相关问题