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?
c# json json.net visitor-pattern
|
show 7 more comments
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?
c# json json.net visitor-pattern
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 toC#
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 acase JValue value: result.Add(value); break;
in the loop inVisitInternal(...)
– Henrik Hansen
Dec 1 at 8:21
1
@HenrikHansen sorry, for the confusion, I only abandoned theJsonTextReader
for transforming, I moved it's logic to theJsonVisitor
but both are using thePrettyTypeResolver
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
|
show 7 more comments
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?
c# json json.net visitor-pattern
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
c# json json.net visitor-pattern
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 toC#
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 acase JValue value: result.Add(value); break;
in the loop inVisitInternal(...)
– Henrik Hansen
Dec 1 at 8:21
1
@HenrikHansen sorry, for the confusion, I only abandoned theJsonTextReader
for transforming, I moved it's logic to theJsonVisitor
but both are using thePrettyTypeResolver
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
|
show 7 more comments
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 toC#
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 acase JValue value: result.Add(value); break;
in the loop inVisitInternal(...)
– Henrik Hansen
Dec 1 at 8:21
1
@HenrikHansen sorry, for the confusion, I only abandoned theJsonTextReader
for transforming, I moved it's logic to theJsonVisitor
but both are using thePrettyTypeResolver
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
|
show 7 more comments
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 inVisitInternal(...)
– 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 theJsonVisitor
but both are using thePrettyTypeResolver
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