Skip to content

Commit 41f07af

Browse files
committed
fix #2155 if JArray infers a date time on a string it parses it to datetime, if the user wants the original string representation they are out of luck because json.net will only yield a cultural to string on the Date JTokenTimes
1 parent 1722642 commit 41f07af

File tree

4 files changed

+38
-10
lines changed

4 files changed

+38
-10
lines changed

src/Nest/CommonAbstractions/Fields/FieldValues.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
using Newtonsoft.Json;
2-
using Newtonsoft.Json.Linq;
3-
using System;
1+
using System;
42
using System.Collections;
53
using System.Collections.Generic;
4+
using System.IO;
65
using System.Linq;
76
using System.Linq.Expressions;
8-
using System.Text;
9-
using System.Threading.Tasks;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Linq;
109

1110
namespace Nest
1211
{
@@ -51,6 +50,12 @@ public K[] Values<T, K>(Expression<Func<T, K>> objectPath)
5150
return this.FieldArray<K[]>(field);
5251
}
5352

53+
54+
private static JsonSerializer ForceNoDateInferrence = new JsonSerializer
55+
{
56+
DateParseHandling = DateParseHandling.None
57+
};
58+
5459
private K FieldArray<K>(string field)
5560
{
5661
object o;
@@ -60,6 +65,19 @@ private K FieldArray<K>(string field)
6065
if (o is JArray && t.GetInterfaces().Contains(typeof(IEnumerable)))
6166
{
6267
var array = (JArray)o;
68+
if (typeof(K) == typeof(string[]) && array.Count > 0 && array.Any(p=>p.Type == JTokenType.Date))
69+
{
70+
// https://github.com/elastic/elasticsearch-net/issues/2155
71+
// o of type JArray has already decided the values are dates so there is no
72+
// way around this.
73+
// incredibly ugly and sad but the only way I found to cover this edgecase
74+
var s = array.Root.ToString();
75+
using (var sr = new StringReader(s))
76+
using (var jr = new JsonTextReader(sr) { DateParseHandling = DateParseHandling.None })
77+
return ForceNoDateInferrence.Deserialize<K>(jr);
78+
79+
return array.Root.ToObject<K>(ForceNoDateInferrence);
80+
}
6381
return array.ToObject<K>();
6482
}
6583
return (K)Convert.ChangeType(o, typeof(K));

src/Tests/Framework/MockData/Project.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
45
using Bogus;
56
using Nest;
@@ -14,6 +15,7 @@ public class Project
1415
public string Description { get; set; }
1516
public StateOfBeing State { get; set; }
1617
public DateTime StartedOn { get; set; }
18+
public string DateString { get; set; }
1719
public DateTime LastActivity { get; set; }
1820
public Developer LeadDeveloper { get; set; }
1921
public IEnumerable<Tag> Tags { get; set; }
@@ -29,6 +31,7 @@ public class Project
2931
.RuleFor(p => p.Description, f => f.Lorem.Paragraphs(3))
3032
.RuleFor(p => p.State, f => f.PickRandom<StateOfBeing>())
3133
.RuleFor(p => p.StartedOn, p => p.Date.Past())
34+
.RuleFor(p => p.DateString, (p, d) => d.StartedOn.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz"))
3235
.RuleFor(p => p.LastActivity, p => p.Date.Recent())
3336
.RuleFor(p => p.LeadDeveloper, p => Developer.Developers[Gimme.Random.Number(0, Developer.Developers.Count -1)])
3437
.RuleFor(p => p.Tags, f => Tag.Generator.Generate(Gimme.Random.Number(2, 50)))
@@ -60,6 +63,7 @@ public class Project
6063
Name = Projects.First().Name,
6164
LeadDeveloper = new Developer() { FirstName = "Martijn", LastName = "Laarman" },
6265
StartedOn = new DateTime(2015, 1, 1),
66+
DateString = new DateTime(2015, 1, 1).ToString(CultureInfo.InvariantCulture),
6367
Location = new SimpleGeoPoint { Lat = 42.1523, Lon = -80.321 }
6468
};
6569

@@ -69,6 +73,7 @@ public class Project
6973
state = "BellyUp",
7074
startedOn = "2015-01-01T00:00:00",
7175
lastActivity = "0001-01-01T00:00:00",
76+
dateString = "2015-01-01T00:00:00",
7277
leadDeveloper = new { gender = "Male", id = 0, firstName = "Martijn", lastName = "Laarman" },
7378
location = new { lat = Instance.Location.Lat, lon = Instance.Location.Lon }
7479
};

src/Tests/Search/Request/FieldsUsageTests.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ namespace Tests.Search.Request
1212
{
1313
/** Allows to selectively load specific stored fields for each document represented by a search hit.
1414
*
15-
* WARNING: The `fields` parameter is about fields that are explicitly marked as stored in the mapping,
16-
* which is off by default and generally not recommended.
15+
* WARNING: The `fields` parameter is about fields that are explicitly marked as stored in the mapping,
16+
* which is off by default and generally not recommended.
1717
* Use <<source-filtering-usage,source filtering>> instead to select subsets of the original source document to be returned.
1818
*
1919
* See the Elasticsearch documentation on {ref_current}/search-request-fields.html[Fields] for more detail.
@@ -31,12 +31,13 @@ public FieldsUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(clu
3131
.Fields(fs => fs
3232
.Field(p => p.Name)
3333
.Field(p => p.StartedOn)
34+
.Field(p => p.DateString)
3435
);
3536

3637
protected override SearchRequest<Project> Initializer =>
3738
new SearchRequest<Project>
3839
{
39-
Fields = Fields<Project>(p => p.Name, p => p.StartedOn)
40+
Fields = Fields<Project>(p => p.Name, p => p.StartedOn, p => p.DateString)
4041
};
4142

4243
[I] protected Task FieldsAreReturned() => this.AssertOnAllResponses(r =>
@@ -45,12 +46,16 @@ [I] protected Task FieldsAreReturned() => this.AssertOnAllResponses(r =>
4546
r.Fields.Count().Should().BeGreaterThan(0);
4647
foreach (var fieldValues in r.Fields)
4748
{
48-
fieldValues.Count().Should().Be(2);
49+
fieldValues.Count().Should().Be(3);
4950
var name = fieldValues.Value<string>(Field<Project>(p => p.Name));
5051
name.Should().NotBeNullOrWhiteSpace();
5152

5253
var dateTime = fieldValues.ValueOf<Project, DateTime>(p => p.StartedOn);
5354
dateTime.Should().BeAfter(default(DateTime));
55+
56+
var dateTimeAsString = fieldValues.ValueOf<Project, string>(p => p.DateString);
57+
dateTimeAsString.Should().NotContain("/")
58+
.And.MatchRegex(@"^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.*$");
5459
}
5560
});
5661
}

src/Tests/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# mode either u (unit test), i (integration test) or m (mixed mode)
22
mode: u
33
# the elasticsearch version that should be started
4-
elasticsearch_version: 2.3.1
4+
elasticsearch_version: 2.3.4
55
# whether we want to forcefully reseed on the node, if you are starting the tests with a node already running
66
force_reseed: true
77
# do not spawn nodes as part of the test setup but rely on a manually started es node being up

0 commit comments

Comments
 (0)