Chaining JSON transformations with visitors











up vote
4
down vote

favorite












I have currently two use-cases that require JSON transformations before it can be deserialized.




  • The first use-case requires changing the custom short type property name from $t to $type and resolving full type names from their aliases. This is the same as in my previous question.

  • The second use-case is about fixing property names that contain whitespaces and need to be trimmed like > Name < instead of >Name< (yeah, I've spent quite some time spotting this mistake in a large file recently).


My previous solution supported only resolving type names and was build with a custom JsonTextReader. Now that I wanted to add another trasnformation to it, I noticed that it cannot be chained because the reader supports forward-only reading and there is no way to add to it.



I've changed my strategy and used the JToken.Parse instead with a couple of visitors that work similar to the ExpressionVisitor.



The first one of them is the JsonVisitor that recursively rebuilds the tree and allows to intercept this process by overriding VisitProperty:



public delegate JToken VisitJsonCallback(JToken token);

public class JsonVisitor
{
public static JToken Visit(JToken token, params JsonVisitor visitors)
{
return visitors.Aggregate(token, (current, visitor) => visitor.Visit(current));
}

public static VisitJsonCallback Create(params JsonVisitor visitors) => jToken => Visit(jToken, visitors);

public JToken Visit(JToken token)
{
return
token is JValue
? token
: VisitInternal(token, CreateJContainer(token.Type));
}

private JToken VisitInternal(JToken token, JContainer result)
{
if (token is JValue)
{
return token;
}

foreach (var element in token)
{
switch (element)
{
case JProperty property:
var visitedProperty = VisitProperty(property);
result.Add(visitedProperty);
break;
default:
result.Add(Visit(element));
break;
}
}
return result;
}

protected virtual JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name, Visit(property.Value));
}

private static JContainer CreateJContainer(JTokenType tokenType)
{
switch (tokenType)
{
case JTokenType.Object: return new JObject();
case JTokenType.Array: return new JArray();
default: throw new ArgumentOutOfRangeException($"Invalid type: {tokenType}");
}
}
}


Since I didn't have any other use cases yet, it's the only overridable API right now but I think it should be easy to extend it in future.



Then I have two more of them that cover both use-cases (here in the reverse order)



public class PropertyNameTrimmer : JsonVisitor
{
protected override JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name.Trim(), Visit(property.Value));
}
}

public class TypeResolver : JsonVisitor
{
private readonly string _typePropertyName;
private readonly Func<string, string> _resolveType;

public TypeResolver(string typePropertyName, Func<string, string> resolveType)
{
_typePropertyName = typePropertyName;
_resolveType = resolveType;
}

protected override JProperty VisitProperty(JProperty property)
{
return
property.Name == _typePropertyName
? new JProperty("$type", _resolveType(property.Value.Value<string>()))
: base.VisitProperty(property);
}
}




Example



I would use them by passing the result from one to the other:



void Main()
{
var json = @"{ ""$t"": ""MyType"", ""User "": ""John"", ""Nested"": { ""$t"": ""OtherType"", "" ZIP"": 12345 }, ""Numbers"": [1, 2, 3 ] }";

var jToken = JToken.Parse(json);
jToken.ToString().Dump("Original");

var propertyNameTrimmer = new PropertyNameTrimmer();
var typeResolver = new TypeResolver("$t", shortName => shortName + "_resolved");

var trimmed = propertyNameTrimmer.Visit(jToken);
trimmed.ToString().Dump(nameof(PropertyNameTrimmer));
var typeResolved = typeResolver.Visit(trimmed);
typeResolved.ToString().Dump(nameof(TypeResolver));
}


So calling them in this order produces:



Original



{
"$t": "MyType",
"User ": "John",
"Nested": {
"$t": "OtherType",
" ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


PropertyNameTrimmer - whitespaces removed from property names



{
"$t": "MyType",
"User": "John",
"Nested": {
"$t": "OtherType",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


TypeResolver - property names changed and types resolved



{
"$type": "MyType_resolved",
"User": "John",
"Nested": {
"$type": "OtherType_resolved",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}




How do you like this solution? Have I missed anything important (but null-checks) and can I improve this in any way?










share|improve this question
























  • Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
    – Pieter Witvoet
    Nov 29 at 9:56










  • @PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
    – t3chb0t
    Nov 29 at 10:02










  • @PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
    – t3chb0t
    Nov 29 at 10:05








  • 1




    A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
    – Henrik Hansen
    Dec 1 at 8:21






  • 1




    @HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
    – t3chb0t
    Dec 1 at 8:30

















up vote
4
down vote

favorite












I have currently two use-cases that require JSON transformations before it can be deserialized.




  • The first use-case requires changing the custom short type property name from $t to $type and resolving full type names from their aliases. This is the same as in my previous question.

  • The second use-case is about fixing property names that contain whitespaces and need to be trimmed like > Name < instead of >Name< (yeah, I've spent quite some time spotting this mistake in a large file recently).


My previous solution supported only resolving type names and was build with a custom JsonTextReader. Now that I wanted to add another trasnformation to it, I noticed that it cannot be chained because the reader supports forward-only reading and there is no way to add to it.



I've changed my strategy and used the JToken.Parse instead with a couple of visitors that work similar to the ExpressionVisitor.



The first one of them is the JsonVisitor that recursively rebuilds the tree and allows to intercept this process by overriding VisitProperty:



public delegate JToken VisitJsonCallback(JToken token);

public class JsonVisitor
{
public static JToken Visit(JToken token, params JsonVisitor visitors)
{
return visitors.Aggregate(token, (current, visitor) => visitor.Visit(current));
}

public static VisitJsonCallback Create(params JsonVisitor visitors) => jToken => Visit(jToken, visitors);

public JToken Visit(JToken token)
{
return
token is JValue
? token
: VisitInternal(token, CreateJContainer(token.Type));
}

private JToken VisitInternal(JToken token, JContainer result)
{
if (token is JValue)
{
return token;
}

foreach (var element in token)
{
switch (element)
{
case JProperty property:
var visitedProperty = VisitProperty(property);
result.Add(visitedProperty);
break;
default:
result.Add(Visit(element));
break;
}
}
return result;
}

protected virtual JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name, Visit(property.Value));
}

private static JContainer CreateJContainer(JTokenType tokenType)
{
switch (tokenType)
{
case JTokenType.Object: return new JObject();
case JTokenType.Array: return new JArray();
default: throw new ArgumentOutOfRangeException($"Invalid type: {tokenType}");
}
}
}


Since I didn't have any other use cases yet, it's the only overridable API right now but I think it should be easy to extend it in future.



Then I have two more of them that cover both use-cases (here in the reverse order)



public class PropertyNameTrimmer : JsonVisitor
{
protected override JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name.Trim(), Visit(property.Value));
}
}

public class TypeResolver : JsonVisitor
{
private readonly string _typePropertyName;
private readonly Func<string, string> _resolveType;

public TypeResolver(string typePropertyName, Func<string, string> resolveType)
{
_typePropertyName = typePropertyName;
_resolveType = resolveType;
}

protected override JProperty VisitProperty(JProperty property)
{
return
property.Name == _typePropertyName
? new JProperty("$type", _resolveType(property.Value.Value<string>()))
: base.VisitProperty(property);
}
}




Example



I would use them by passing the result from one to the other:



void Main()
{
var json = @"{ ""$t"": ""MyType"", ""User "": ""John"", ""Nested"": { ""$t"": ""OtherType"", "" ZIP"": 12345 }, ""Numbers"": [1, 2, 3 ] }";

var jToken = JToken.Parse(json);
jToken.ToString().Dump("Original");

var propertyNameTrimmer = new PropertyNameTrimmer();
var typeResolver = new TypeResolver("$t", shortName => shortName + "_resolved");

var trimmed = propertyNameTrimmer.Visit(jToken);
trimmed.ToString().Dump(nameof(PropertyNameTrimmer));
var typeResolved = typeResolver.Visit(trimmed);
typeResolved.ToString().Dump(nameof(TypeResolver));
}


So calling them in this order produces:



Original



{
"$t": "MyType",
"User ": "John",
"Nested": {
"$t": "OtherType",
" ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


PropertyNameTrimmer - whitespaces removed from property names



{
"$t": "MyType",
"User": "John",
"Nested": {
"$t": "OtherType",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


TypeResolver - property names changed and types resolved



{
"$type": "MyType_resolved",
"User": "John",
"Nested": {
"$type": "OtherType_resolved",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}




How do you like this solution? Have I missed anything important (but null-checks) and can I improve this in any way?










share|improve this question
























  • Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
    – Pieter Witvoet
    Nov 29 at 9:56










  • @PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
    – t3chb0t
    Nov 29 at 10:02










  • @PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
    – t3chb0t
    Nov 29 at 10:05








  • 1




    A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
    – Henrik Hansen
    Dec 1 at 8:21






  • 1




    @HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
    – t3chb0t
    Dec 1 at 8:30















up vote
4
down vote

favorite









up vote
4
down vote

favorite











I have currently two use-cases that require JSON transformations before it can be deserialized.




  • The first use-case requires changing the custom short type property name from $t to $type and resolving full type names from their aliases. This is the same as in my previous question.

  • The second use-case is about fixing property names that contain whitespaces and need to be trimmed like > Name < instead of >Name< (yeah, I've spent quite some time spotting this mistake in a large file recently).


My previous solution supported only resolving type names and was build with a custom JsonTextReader. Now that I wanted to add another trasnformation to it, I noticed that it cannot be chained because the reader supports forward-only reading and there is no way to add to it.



I've changed my strategy and used the JToken.Parse instead with a couple of visitors that work similar to the ExpressionVisitor.



The first one of them is the JsonVisitor that recursively rebuilds the tree and allows to intercept this process by overriding VisitProperty:



public delegate JToken VisitJsonCallback(JToken token);

public class JsonVisitor
{
public static JToken Visit(JToken token, params JsonVisitor visitors)
{
return visitors.Aggregate(token, (current, visitor) => visitor.Visit(current));
}

public static VisitJsonCallback Create(params JsonVisitor visitors) => jToken => Visit(jToken, visitors);

public JToken Visit(JToken token)
{
return
token is JValue
? token
: VisitInternal(token, CreateJContainer(token.Type));
}

private JToken VisitInternal(JToken token, JContainer result)
{
if (token is JValue)
{
return token;
}

foreach (var element in token)
{
switch (element)
{
case JProperty property:
var visitedProperty = VisitProperty(property);
result.Add(visitedProperty);
break;
default:
result.Add(Visit(element));
break;
}
}
return result;
}

protected virtual JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name, Visit(property.Value));
}

private static JContainer CreateJContainer(JTokenType tokenType)
{
switch (tokenType)
{
case JTokenType.Object: return new JObject();
case JTokenType.Array: return new JArray();
default: throw new ArgumentOutOfRangeException($"Invalid type: {tokenType}");
}
}
}


Since I didn't have any other use cases yet, it's the only overridable API right now but I think it should be easy to extend it in future.



Then I have two more of them that cover both use-cases (here in the reverse order)



public class PropertyNameTrimmer : JsonVisitor
{
protected override JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name.Trim(), Visit(property.Value));
}
}

public class TypeResolver : JsonVisitor
{
private readonly string _typePropertyName;
private readonly Func<string, string> _resolveType;

public TypeResolver(string typePropertyName, Func<string, string> resolveType)
{
_typePropertyName = typePropertyName;
_resolveType = resolveType;
}

protected override JProperty VisitProperty(JProperty property)
{
return
property.Name == _typePropertyName
? new JProperty("$type", _resolveType(property.Value.Value<string>()))
: base.VisitProperty(property);
}
}




Example



I would use them by passing the result from one to the other:



void Main()
{
var json = @"{ ""$t"": ""MyType"", ""User "": ""John"", ""Nested"": { ""$t"": ""OtherType"", "" ZIP"": 12345 }, ""Numbers"": [1, 2, 3 ] }";

var jToken = JToken.Parse(json);
jToken.ToString().Dump("Original");

var propertyNameTrimmer = new PropertyNameTrimmer();
var typeResolver = new TypeResolver("$t", shortName => shortName + "_resolved");

var trimmed = propertyNameTrimmer.Visit(jToken);
trimmed.ToString().Dump(nameof(PropertyNameTrimmer));
var typeResolved = typeResolver.Visit(trimmed);
typeResolved.ToString().Dump(nameof(TypeResolver));
}


So calling them in this order produces:



Original



{
"$t": "MyType",
"User ": "John",
"Nested": {
"$t": "OtherType",
" ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


PropertyNameTrimmer - whitespaces removed from property names



{
"$t": "MyType",
"User": "John",
"Nested": {
"$t": "OtherType",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


TypeResolver - property names changed and types resolved



{
"$type": "MyType_resolved",
"User": "John",
"Nested": {
"$type": "OtherType_resolved",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}




How do you like this solution? Have I missed anything important (but null-checks) and can I improve this in any way?










share|improve this question















I have currently two use-cases that require JSON transformations before it can be deserialized.




  • The first use-case requires changing the custom short type property name from $t to $type and resolving full type names from their aliases. This is the same as in my previous question.

  • The second use-case is about fixing property names that contain whitespaces and need to be trimmed like > Name < instead of >Name< (yeah, I've spent quite some time spotting this mistake in a large file recently).


My previous solution supported only resolving type names and was build with a custom JsonTextReader. Now that I wanted to add another trasnformation to it, I noticed that it cannot be chained because the reader supports forward-only reading and there is no way to add to it.



I've changed my strategy and used the JToken.Parse instead with a couple of visitors that work similar to the ExpressionVisitor.



The first one of them is the JsonVisitor that recursively rebuilds the tree and allows to intercept this process by overriding VisitProperty:



public delegate JToken VisitJsonCallback(JToken token);

public class JsonVisitor
{
public static JToken Visit(JToken token, params JsonVisitor visitors)
{
return visitors.Aggregate(token, (current, visitor) => visitor.Visit(current));
}

public static VisitJsonCallback Create(params JsonVisitor visitors) => jToken => Visit(jToken, visitors);

public JToken Visit(JToken token)
{
return
token is JValue
? token
: VisitInternal(token, CreateJContainer(token.Type));
}

private JToken VisitInternal(JToken token, JContainer result)
{
if (token is JValue)
{
return token;
}

foreach (var element in token)
{
switch (element)
{
case JProperty property:
var visitedProperty = VisitProperty(property);
result.Add(visitedProperty);
break;
default:
result.Add(Visit(element));
break;
}
}
return result;
}

protected virtual JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name, Visit(property.Value));
}

private static JContainer CreateJContainer(JTokenType tokenType)
{
switch (tokenType)
{
case JTokenType.Object: return new JObject();
case JTokenType.Array: return new JArray();
default: throw new ArgumentOutOfRangeException($"Invalid type: {tokenType}");
}
}
}


Since I didn't have any other use cases yet, it's the only overridable API right now but I think it should be easy to extend it in future.



Then I have two more of them that cover both use-cases (here in the reverse order)



public class PropertyNameTrimmer : JsonVisitor
{
protected override JProperty VisitProperty(JProperty property)
{
return new JProperty(property.Name.Trim(), Visit(property.Value));
}
}

public class TypeResolver : JsonVisitor
{
private readonly string _typePropertyName;
private readonly Func<string, string> _resolveType;

public TypeResolver(string typePropertyName, Func<string, string> resolveType)
{
_typePropertyName = typePropertyName;
_resolveType = resolveType;
}

protected override JProperty VisitProperty(JProperty property)
{
return
property.Name == _typePropertyName
? new JProperty("$type", _resolveType(property.Value.Value<string>()))
: base.VisitProperty(property);
}
}




Example



I would use them by passing the result from one to the other:



void Main()
{
var json = @"{ ""$t"": ""MyType"", ""User "": ""John"", ""Nested"": { ""$t"": ""OtherType"", "" ZIP"": 12345 }, ""Numbers"": [1, 2, 3 ] }";

var jToken = JToken.Parse(json);
jToken.ToString().Dump("Original");

var propertyNameTrimmer = new PropertyNameTrimmer();
var typeResolver = new TypeResolver("$t", shortName => shortName + "_resolved");

var trimmed = propertyNameTrimmer.Visit(jToken);
trimmed.ToString().Dump(nameof(PropertyNameTrimmer));
var typeResolved = typeResolver.Visit(trimmed);
typeResolved.ToString().Dump(nameof(TypeResolver));
}


So calling them in this order produces:



Original



{
"$t": "MyType",
"User ": "John",
"Nested": {
"$t": "OtherType",
" ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


PropertyNameTrimmer - whitespaces removed from property names



{
"$t": "MyType",
"User": "John",
"Nested": {
"$t": "OtherType",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}


TypeResolver - property names changed and types resolved



{
"$type": "MyType_resolved",
"User": "John",
"Nested": {
"$type": "OtherType_resolved",
"ZIP": 12345
},
"Numbers": [
1,
2,
3
]
}




How do you like this solution? Have I missed anything important (but null-checks) and can I improve this in any way?







c# json json.net visitor-pattern






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 29 at 6:43

























asked Nov 28 at 19:09









t3chb0t

33.7k746110




33.7k746110












  • Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
    – Pieter Witvoet
    Nov 29 at 9:56










  • @PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
    – t3chb0t
    Nov 29 at 10:02










  • @PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
    – t3chb0t
    Nov 29 at 10:05








  • 1




    A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
    – Henrik Hansen
    Dec 1 at 8:21






  • 1




    @HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
    – t3chb0t
    Dec 1 at 8:30




















  • Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
    – Pieter Witvoet
    Nov 29 at 9:56










  • @PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
    – t3chb0t
    Nov 29 at 10:02










  • @PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
    – t3chb0t
    Nov 29 at 10:05








  • 1




    A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
    – Henrik Hansen
    Dec 1 at 8:21






  • 1




    @HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
    – t3chb0t
    Dec 1 at 8:30


















Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
– Pieter Witvoet
Nov 29 at 9:56




Your visitor looks fine for what it does, but why rewrite property names instead of throwing an exception: "Unknown property: 'User ' at position ..."? That also catches other problems like typos ("Usr") and case differences ("Zip"), which are difficult to auto-correct due to ambiguity.
– Pieter Witvoet
Nov 29 at 9:56












@PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
– t3chb0t
Nov 29 at 10:02




@PieterWitvoet yeah, it's a tough decision and I actually wasn't sure (and I'm still not) which one would be better, fixing or throwing exceptions. I was also going to allow dashes in property names that I would rewrite to C# valid names. Typos are difficult to handle anyway because if a property is optional it's hard to say whether it's something else or a typo. I guess I cannot eliminate all mistakes but at least reduce the probability. Btw, json is case-insensitive.
– t3chb0t
Nov 29 at 10:02












@PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
– t3chb0t
Nov 29 at 10:05






@PieterWitvoet for a complete validation I'll probably should use json-schema but I'm not sure about its license and whether I could use it at work... most likely not so I'm not even trying but even when using json-schema I still need to rewrite some parts of it ;-)
– t3chb0t
Nov 29 at 10:05






1




1




A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
– Henrik Hansen
Dec 1 at 8:21




A tiny improvement could be to insert a case JValue value: result.Add(value); break; in the loop in VisitInternal(...)
– Henrik Hansen
Dec 1 at 8:21




1




1




@HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
– t3chb0t
Dec 1 at 8:30






@HenrikHansen sorry, for the confusion, I only abandoned the JsonTextReader for transforming, I moved it's logic to the JsonVisitor but both are using the PrettyTypeResolver as a dependency. This is the nice part when you have separated everything and can change one implementation and still reuse the others ;-)
– t3chb0t
Dec 1 at 8:30

















active

oldest

votes











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208641%2fchaining-json-transformations-with-visitors%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208641%2fchaining-json-transformations-with-visitors%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Список кардиналов, возведённых папой римским Каликстом III

Deduzione

Mysql.sock missing - “Can't connect to local MySQL server through socket”