-
-
Notifications
You must be signed in to change notification settings - Fork 497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to set custom Anchor names? #939
Comments
You could keep track of different counters per object type in a custom alias provider. That should give you what you want. |
Are there any docs or examples, how to implement/use such an custom Alias provider and attach it to the Serializer? I have found some other threads with an equivalent idea/problem, but I'm not to came to a conclusion myself, because there're only hints but no example how to implement such a mechanism. |
When I get to stable internet (next week) I’ll put together an example for you. |
Ok, here you go. using System.Collections.Concurrent;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.ObjectGraphVisitors;
var serializer = new SerializerBuilder()
.WithoutPreProcessingPhaseObjectGraphVisitor<YamlDotNet.Serialization.ObjectGraphVisitors.AnchorAssigner>()
.WithPreProcessingPhaseObjectGraphVisitor(new AnchorAssigner(Array.Empty<IYamlTypeConverter>()))
.WithoutEmissionPhaseObjectGraphVisitor<AnchorAssigningObjectGraphVisitor>()
.WithEmissionPhaseObjectGraphVisitor((EmissionPhaseObjectGraphVisitorArgs args) => new AnchorAssigningObjectGraphVisitor(args.InnerVisitor, args.EventEmitter, args.GetPreProcessingPhaseObjectGraphVisitor<AnchorAssigner>()))
.Build();
var o = new Outer();
AddInner(new Inner1 { Data = "test1" }, x => o.A.Add(x));
AddInner(new Inner1 { Data = "test2" }, x => o.A.Add(x));
AddInner(new Inner1 { Data = "test3" }, x => o.A.Add(x));
AddInner(new Inner1 { Data = "test4" }, x => o.A.Add(x));
AddInner(new Inner1 { Data = "test5" }, x => o.A.Add(x));
AddInner(new Inner2 { Data = "test1" }, x => o.B.Add(x));
AddInner(new Inner2 { Data = "test2" }, x => o.B.Add(x));
AddInner(new Inner2 { Data = "test3" }, x => o.B.Add(x));
AddInner(new Inner2 { Data = "test4" }, x => o.B.Add(x));
AddInner(new Inner2 { Data = "test5" }, x => o.B.Add(x));
AddInner(new Inner3 { Data = "test1" }, x => o.C.Add(x));
AddInner(new Inner3 { Data = "test2" }, x => o.C.Add(x));
AddInner(new Inner3 { Data = "test3" }, x => o.C.Add(x));
AddInner(new Inner3 { Data = "test4" }, x => o.C.Add(x));
AddInner(new Inner3 { Data = "test5" }, x => o.C.Add(x));
Console.WriteLine(serializer.Serialize(o));
void AddInner<TType>(TType inner, Action<TType> adder)
{
for (var i = 0; i < 10; i++)
{
adder(inner);
}
}
public class Outer
{
public List<Inner1> A { get; set; } = new List<Inner1>();
public List<Inner2> B { get; set; } = new List<Inner2>();
public List<Inner3> C { get; set; } = new List<Inner3>();
}
public class Inner1
{
public string Data { get; set; }
}
public class Inner2
{
public string Data { get; set; }
}
public class Inner3
{
public string Data { get; set; }
}
public class AnchorAssigner : PreProcessingPhaseObjectGraphVisitorSkeleton, IAliasProvider
{
private class AnchorAssignment
{
public AnchorName Anchor;
}
private readonly ConcurrentDictionary<Type, IDictionary<object, AnchorAssignment>> typeAssignments = new ConcurrentDictionary<Type, IDictionary<object, AnchorAssignment>>();
public AnchorAssigner(IEnumerable<IYamlTypeConverter> typeConverters)
: base(typeConverters)
{
}
protected override bool Enter(IObjectDescriptor value, ObjectSerializer serializers)
{
if (value.Value != null)
{
var type = value.Value.GetType();
var assignments = typeAssignments.GetOrAdd(type, (_) => new ConcurrentDictionary<object, AnchorAssignment>());
if (assignments.TryGetValue(value.Value, out var assignment))
{
if (assignment.Anchor.IsEmpty)
{
assignment.Anchor = new AnchorName($"{type.Name}-{assignments.Count}");
}
return false;
}
}
return true;
}
protected override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value, ObjectSerializer serializers)
{
return true;
}
protected override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, ObjectSerializer serializers)
{
return true;
}
protected override void VisitScalar(IObjectDescriptor scalar, ObjectSerializer serializers)
{
// Do not assign anchors to scalars
}
protected override void VisitMappingStart(IObjectDescriptor mapping, Type keyType, Type valueType, ObjectSerializer serializers)
{
VisitObject(mapping);
}
protected override void VisitMappingEnd(IObjectDescriptor mapping, ObjectSerializer serializers) { }
protected override void VisitSequenceStart(IObjectDescriptor sequence, Type elementType, ObjectSerializer serializers)
{
VisitObject(sequence);
}
protected override void VisitSequenceEnd(IObjectDescriptor sequence, ObjectSerializer serializers) { }
private void VisitObject(IObjectDescriptor value)
{
if (value.Value != null)
{
var assignments = typeAssignments.GetOrAdd(value.Value.GetType(), (_) => new Dictionary<object, AnchorAssignment>());
assignments.Add(value.Value, new AnchorAssignment());
}
}
AnchorName IAliasProvider.GetAlias(object target)
{
var assignments = typeAssignments.GetOrAdd(target.GetType(), (_) => new Dictionary<object, AnchorAssignment>());
if (target != null && assignments.TryGetValue(target, out var assignment))
{
return assignment.Anchor;
}
return AnchorName.Empty;
}
} Results in A:
- &Inner1-1
Data: test1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- *Inner1-1
- &Inner1-2
Data: test2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- *Inner1-2
- &Inner1-3
Data: test3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- *Inner1-3
- &Inner1-4
Data: test4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- *Inner1-4
- &Inner1-5
Data: test5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
- *Inner1-5
B:
- &Inner2-1
Data: test1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- *Inner2-1
- &Inner2-2
Data: test2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- *Inner2-2
- &Inner2-3
Data: test3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- *Inner2-3
- &Inner2-4
Data: test4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- *Inner2-4
- &Inner2-5
Data: test5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
- *Inner2-5
C:
- &Inner3-1
Data: test1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- *Inner3-1
- &Inner3-2
Data: test2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- *Inner3-2
- &Inner3-3
Data: test3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- *Inner3-3
- &Inner3-4
Data: test4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- *Inner3-4
- &Inner3-5
Data: test5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5
- *Inner3-5 |
Works like a charme - Thank you. |
After some more work on our project, we have realised, that the code above does not work correctly. The problem is, that all anchor names are the same PER TYPE, set with the maximum count of this type. Here a short example - input:
output
If we will deactivate the custom anchor assigner, we got out different objects with different anchor names, as expected. So I don't think there is a problem in our data structure at all.
|
Is there any possibility, to step into the serialization process and modify the rules,
how anchor tags in the YAML are named? Let me show a little Example.
Maybe with a naming rule, which iterate per "dataclass" type.
What I want:
What I actually get:
The text was updated successfully, but these errors were encountered: