❔ NewtownSoft.JSON and classes that need references to its owner

I have two classes. Class A owns instances of Class B. But each instance of Class B needs a reference back to the instance of Class A that created it. Can System.Text.Json.JsonSerializer do that?
62 Replies
JakenVeina
JakenVeina14mo ago
depends how it's structured
Will Pittenger
Will PittengerOP14mo ago
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
JakenVeina
JakenVeina14mo ago
public class Parent
{
public required string Name { get; init; }

public required IReadOnlyList<Child> Children { get; init; }
}

public class Child
{
public required string Name { get; init; }

public required Parent Parent { get; init; }
}
public class Parent
{
public required string Name { get; init; }

public required IReadOnlyList<Child> Children { get; init; }
}

public class Child
{
public required string Name { get; init; }

public required Parent Parent { get; init; }
}
this ought to work, if you enable reference handling wouldn't work if you were going to, say, use constructor initialization, instead of init properties
Will Pittenger
Will PittengerOP14mo ago
OK.
Will Pittenger
Will PittengerOP14mo ago
So would that work even if the value is only a string? See the final example in https://discord.com/channels/143867839282020352/1157002592119947374.
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
JakenVeina
JakenVeina14mo ago
even if what value is only a string? not sure what you mean
Will Pittenger
Will PittengerOP14mo ago
The first sample here would produce the same results as the second:
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
}
]
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
}
]
[
{
"wrapped": "ss",
"some other value": "default"
},
{
"wrapped": "value",
"some other value": "value2"
}
]
[
{
"wrapped": "ss",
"some other value": "default"
},
{
"wrapped": "value",
"some other value": "value2"
}
]
JakenVeina
JakenVeina14mo ago
in what regard? produce how? what would produce that?
Will Pittenger
Will PittengerOP14mo ago
Well, I was going to use an implicit typecast operator as was suggested in the thread I linked earlier, https://discord.com/channels/143867839282020352/1157002592119947374. But that operator can't work as it doesn't have a reference to the owner.
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
Will Pittenger
Will PittengerOP14mo ago
The string is a simplified version of the long form.
JakenVeina
JakenVeina14mo ago
okay, like what is the actual goal here are you trying to serialize JSON or deserialize?
Will Pittenger
Will PittengerOP14mo ago
Deserialize.
JakenVeina
JakenVeina14mo ago
what's the JSON you want to deserialize?
Will Pittenger
Will PittengerOP14mo ago
???? I showed you that above.
JakenVeina
JakenVeina14mo ago
not really you posted two snippets and said that "they produce the same result" which one represents JSON that you receive and need to deserialize? both of them?
Will Pittenger
Will PittengerOP14mo ago
Well, both could.
JakenVeina
JakenVeina14mo ago
k
Will Pittenger
Will PittengerOP14mo ago
They're both valid.
JakenVeina
JakenVeina14mo ago
and you're using Newtonsoft? or System.Text.Json?
Will Pittenger
Will PittengerOP14mo ago
Yes. Well, I see System.Text.Json, but I had to install a package to get it.
JakenVeina
JakenVeina14mo ago
uhh, same as you did for Newtonsoft anyway, so the most straightforward implementation is...
public class MyJsonRecord
{
[JsonProperty("wrapped")]
public required string Wrapped { get; init; }

[JsonProperty("some other value")]
public required string SomeOtherValue { get; init; }
}
public class MyJsonRecord
{
[JsonProperty("wrapped")]
public required string Wrapped { get; init; }

[JsonProperty("some other value")]
public required string SomeOtherValue { get; init; }
}
var json1 = @"[""ss"",{""wrapped"": ""value"",""some other value"": ""value2""}]";
var json2 = @"[{""wrapped"": ""ss"",""some other value"": ""default""},{""wrapped"": ""value"",""some other value"": ""value2""}]";

var result1 = JsonConvert.DeserializeObject<List<object>>(json1);
var result2 = JsonConvert.DeserializeObject<List<object>>(json2);
var json1 = @"[""ss"",{""wrapped"": ""value"",""some other value"": ""value2""}]";
var json2 = @"[{""wrapped"": ""ss"",""some other value"": ""default""},{""wrapped"": ""value"",""some other value"": ""value2""}]";

var result1 = JsonConvert.DeserializeObject<List<object>>(json1);
var result2 = JsonConvert.DeserializeObject<List<object>>(json2);
Will Pittenger
Will PittengerOP14mo ago
What about the owner field?
JakenVeina
JakenVeina14mo ago
what is "the owner field"?
Will Pittenger
Will PittengerOP14mo ago
I told you the class in the list needs a reference to the object that owns it.
JakenVeina
JakenVeina14mo ago
which is what? you said that, but where is that represented in the JSON?
Will Pittenger
Will PittengerOP14mo ago
It's more like:
C#
class Y
{
// cooresponsds to the array in the file
System.Collections.Generic.List<X> list = new();
}
class X
{
public readonly Y owner;
public required string Wrapped {get; init;}

public required string SomeOtherValue{get; init;}
}
C#
class Y
{
// cooresponsds to the array in the file
System.Collections.Generic.List<X> list = new();
}
class X
{
public readonly Y owner;
public required string Wrapped {get; init;}

public required string SomeOtherValue{get; init;}
}
JakenVeina
JakenVeina14mo ago
okay
Will Pittenger
Will PittengerOP14mo ago
It isn't. I'm needing a way to pass it in.
JakenVeina
JakenVeina14mo ago
that would be the most effective thing to do, yes technically, you could build a whole custom JSON parser that is smart enough to populate it on construction but it's really not the job of the JSON parser, anyway that owner field isn't part of the data, it's part of your business model populate it in your business layer
Will Pittenger
Will PittengerOP14mo ago
Unfortunately, it needs to be set when the instance is created.
JakenVeina
JakenVeina14mo ago
why?
Will Pittenger
Will PittengerOP14mo ago
I want it public readonly.
JakenVeina
JakenVeina14mo ago
ergo, it doesn't need to be
Will Pittenger
Will PittengerOP14mo ago
It isn't valid without it.
JakenVeina
JakenVeina14mo ago
for one, it shouldn't be a field for two, initialzing it after construction doesn't make it not valid so long as you don't publish the instance for consumption before initialization is complete
public class Y
{
public static Y ParseFromJson(string json)
{
var y = new Y()
{
Items = JsonConverter.Deserialize<List<object>>(sourceJson);
};

foreach(var item in items)
if (item is X x)
x.Owner = y;

return y;
}

private Y() {}

public required IReadOnlyList<object> Items { get; init; }
}

public class X
{
internal X() {}

public Y Owner { get; internal set; }
= null!;

public required string Wrapped {get; init;}

public required string SomeOtherValue{get; init;}
}
public class Y
{
public static Y ParseFromJson(string json)
{
var y = new Y()
{
Items = JsonConverter.Deserialize<List<object>>(sourceJson);
};

foreach(var item in items)
if (item is X x)
x.Owner = y;

return y;
}

private Y() {}

public required IReadOnlyList<object> Items { get; init; }
}

public class X
{
internal X() {}

public Y Owner { get; internal set; }
= null!;

public required string Wrapped {get; init;}

public required string SomeOtherValue{get; init;}
}
TheRanger
TheRanger14mo ago
well, would public Y Owner { get; private set; } be fine with you?
Will Pittenger
Will PittengerOP14mo ago
Somewhat. What about the init accessor for the owner?
TheRanger
TheRanger14mo ago
well i found a weird way,
public class Y
{
public System.Collections.Generic.List<X> list = new();
public static Y LatestInstance {get; private set;}

public static Y Deserialize(string json)
{
return new Y()
{
list = JsonConvert.DeserializeObject<System.Collections.Generic.List<X>>(json),
};
}

public Y()
{
LatestInstance = this;
}
}

public class X
{
public required Y Owner {get; init;}
[JsonProperty("wrapped")]
public required string Wrapped { get; init; }

[JsonProperty("some other value")]
public required string SomeOtherValue { get; init; }

public static explicit operator X(string str) => Parse(str);

public static X Parse(string str)
{
return new X
{
Wrapped = str,
Owner = Y.LatestInstance,
SomeOtherValue = "default",
};
}

public X()
{
Owner = Y.LatestInstance;
}
}
public class Y
{
public System.Collections.Generic.List<X> list = new();
public static Y LatestInstance {get; private set;}

public static Y Deserialize(string json)
{
return new Y()
{
list = JsonConvert.DeserializeObject<System.Collections.Generic.List<X>>(json),
};
}

public Y()
{
LatestInstance = this;
}
}

public class X
{
public required Y Owner {get; init;}
[JsonProperty("wrapped")]
public required string Wrapped { get; init; }

[JsonProperty("some other value")]
public required string SomeOtherValue { get; init; }

public static explicit operator X(string str) => Parse(str);

public static X Parse(string str)
{
return new X
{
Wrapped = str,
Owner = Y.LatestInstance,
SomeOtherValue = "default",
};
}

public X()
{
Owner = Y.LatestInstance;
}
}
its probably dumb, but probably only the way with the required attribute
Will Pittenger
Will PittengerOP14mo ago
BTW: Your X doesn't declare owner. Oh it does. I was expecting a [JsonIgnore] on it as the owner isn't in the JSON.
TheRanger
TheRanger14mo ago
ur json doesnt have a Owner field so it will be null as default you can put [JsonIgnore] in case
Will Pittenger
Will PittengerOP14mo ago
Unfortunately, all instances need to have an valid owner--even if the full version of the class was used.
TheRanger
TheRanger14mo ago
what do u mean? isn't the owner valid in your json?
Will Pittenger
Will PittengerOP14mo ago
Go back to the original short form mentioned in https://discord.com/channels/143867839282020352/1157109068423507988/1157150777404571708. The first value in the array would end up with an owner using your code. But not the second. Both must have owners. (In this case, the same owner.)
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
TheRanger
TheRanger14mo ago
ah so u have 2 forms of that in the same json?
Will Pittenger
Will PittengerOP14mo ago
Yes.
TheRanger
TheRanger14mo ago
in the same list/array?
Will Pittenger
Will PittengerOP14mo ago
Yes. Only the full form will ever be written. But the short form might be on the website as a way of specifying predefined entries.
TheRanger
TheRanger14mo ago
i tested this
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
},
{
"t":
{
"wrapped": "ss",
"some other value": "default"
}
},
{
"t":
{
"wrapped": "value",
"some other value": "value2"
}
}
]
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
},
{
"t":
{
"wrapped": "ss",
"some other value": "default"
}
},
{
"t":
{
"wrapped": "value",
"some other value": "value2"
}
}
]
all have the same owner but how do you think the last 2 elements in the array would be converted with this code
Will Pittenger
Will PittengerOP14mo ago
No.
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
}
]
[
"ss",
{
"wrapped": "value",
"some other value": "value2"
}
]
Those are both elements in the array. That's equivalent to:
[
{
"wrapped": "ss",
"some other value": "default"
},
{
"wrapped": "value",
"some other value": "value2"
}
]
[
{
"wrapped": "ss",
"some other value": "default"
},
{
"wrapped": "value",
"some other value": "value2"
}
]
TheRanger
TheRanger14mo ago
so u dont have something like this in ur json anymore?
"t":
{
"wrapped": "value",
"some other value": "value2"
}
"t":
{
"wrapped": "value",
"some other value": "value2"
}
Will Pittenger
Will PittengerOP14mo ago
I think that was a mistake in the other class.
TheRanger
TheRanger14mo ago
then what original short form were u talking about
Will Pittenger
Will PittengerOP14mo ago
this
TheRanger
TheRanger14mo ago
Yes, that is what i used both have same owner
Will Pittenger
Will PittengerOP14mo ago
So you didn't do anything special?
TheRanger
TheRanger14mo ago
did you use this code?
Will Pittenger
Will PittengerOP14mo ago
Didn't try it yet. Been busy elsewhere while waiting on the other issues. Got started on those.
TheRanger
TheRanger14mo ago
🤷🏻‍♂️
Will Pittenger
Will PittengerOP14mo ago
Sorry. I also still on the fence if your solution is acceptable. It does have a bit of code smell.
TheRanger
TheRanger14mo ago
well, i tested it, so it works fine for me
Will Pittenger
Will PittengerOP14mo ago
OK.
Accord
Accord14mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server