diff --git a/Examples/FixToJson/Program.cs b/Examples/FixToJson/Program.cs index 2142ba359..d02cedf3e 100644 --- a/Examples/FixToJson/Program.cs +++ b/Examples/FixToJson/Program.cs @@ -23,7 +23,7 @@ static void FixToJson( { line = line.Trim(); msg.FromString(line, false, sessionDataDictionary, appDataDictionary, msgFactory); - Console.WriteLine(comma + msg.ToJSON(humanReadableValues: humanReadableValues)); + Console.WriteLine(comma + msg.ToJSON(convertEnumsToDescriptions: humanReadableValues)); comma = ","; } } diff --git a/QuickFIXn/Message/Message.cs b/QuickFIXn/Message/Message.cs index 333f286b4..5d22087b0 100644 --- a/QuickFIXn/Message/Message.cs +++ b/QuickFIXn/Message/Message.cs @@ -946,17 +946,22 @@ public string ToXML(DD? dataDictionary = null) /// Per the FIX JSON Encoding spec, tags are converted to human-readable form, but values are not. /// /// Needed if you want tag names emitted or humanReadableValues to work - /// - /// True will cause enums to be converted to human strings. - /// Will not (and cannot!) work if dataDictionary is null. + /// + /// True will cause enums to be converted to their description strings, but only if dataDictionary is provided. + /// If true and dataDictionary is null, then throws an ArgumentNullException. /// /// a JSON string - public string ToJSON(DD? dataDictionary = null, bool humanReadableValues = false) - { - StringBuilder sb = new StringBuilder().Append("{").Append("\"Header\":{"); - FieldMapToJSON(sb, dataDictionary, Header, humanReadableValues).Append("},\"Body\":{"); - FieldMapToJSON(sb, dataDictionary, this, humanReadableValues).Append("},\"Trailer\":{"); - FieldMapToJSON(sb, dataDictionary, Trailer, humanReadableValues).Append("}}"); + public string ToJSON(DD? dataDictionary = null, bool convertEnumsToDescriptions = false) { + if (convertEnumsToDescriptions && dataDictionary is null) { + throw new ArgumentNullException( + nameof(dataDictionary), + $"Must be non-null if '{nameof(convertEnumsToDescriptions)}' is true."); + } + + StringBuilder sb = new StringBuilder().Append('{').Append("\"Header\":{"); + FieldMapToJSON(sb, dataDictionary, Header, convertEnumsToDescriptions).Append("},\"Body\":{"); + FieldMapToJSON(sb, dataDictionary, this, convertEnumsToDescriptions).Append("},\"Trailer\":{"); + FieldMapToJSON(sb, dataDictionary, Trailer, convertEnumsToDescriptions).Append("}}"); return sb.ToString(); } } diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 43dc9201e..9f09140c0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -16,10 +16,13 @@ What's New ### (next release) **Breaking changes** -* #878 - corrections to tag 45 "Side" in various DDs (gbirchmeier) +* #878 - corrections to tag 45 "Side" in various DDs (gbirchmeier) - most people won't notice, easy fix if they do * fix typo in FIX50 and FIX50SP1: `CROSS_SHORT_EXXMPT` fixed to `CROSS_SHORT_EXEMPT` * correction in FIX41 and FIX42: `D` to `UNDISCLOSED` +**Non-breaking changes** +* #877 - throw an exception if Message.ToJSON(dd=null,convertEnumsToDescriptions=true) is called (gbirchmeier) + ### v1.12.0 **Breaking changes** @@ -75,7 +78,6 @@ What's New * Some classes were internalized, but I can't imagine people are using them in their app code. * See details/explanation at https://github.com/connamara/quickfixn/pull/830 - **Non-breaking changes** * #400 - added DDTool, a C#-based codegen, and deleted Ruby-based generator (gbirchmeier) * #811 - convert AT platform to be NUnit-based, get rid of Ruby runner (Rob-Hague) diff --git a/UnitTests/MessageToXmlTests.cs b/UnitTests/MessageToXmlTests.cs index 7596b5b4d..8d22a3b89 100644 --- a/UnitTests/MessageToXmlTests.cs +++ b/UnitTests/MessageToXmlTests.cs @@ -88,14 +88,21 @@ public void ToJSONWithGroupsTest() QuickFix.FIX44.ExecutionReport msg = new QuickFix.FIX44.ExecutionReport(); msg.FromString(msgStr, true, dd, dd, null); // <-- null factory! - string expected = "{\"Header\":{\"BeginString\":\"FIX.4.4\",\"BodyLength\":\"638\",\"MsgSeqNum\":\"360\",\"MsgType\":\"8\",\"SenderCompID\":\"BLPTSOX\",\"SendingTime\":\"20130321-15:21:23\",\"TargetCompID\":\"THINKTSOX\",\"TargetSubID\":\"6804469\",\"DeliverToCompID\":\"ZERO\"},\"Body\":{\"AvgPx\":\"122.255\",\"ClOrdID\":\"61101189\",\"CumQty\":\"1990000\",\"Currency\":\"GBP\",\"ExecID\":\"VCON:20130321:50018:5:12\",\"SecurityIDSource\":\"4\",\"LastPx\":\"122.255\",\"LastQty\":\"1990000\",\"OrderID\":\"116\",\"OrderQty\":\"1990000\",\"OrdStatus\":\"2\",\"SecurityID\":\"GB0032452392\",\"Side\":\"1\",\"Symbol\":\"[N/A]\",\"TransactTime\":\"20130321-15:21:23\",\"SettlDate\":\"20130322\",\"TradeDate\":\"20130321\",\"Issuer\":\"UK TSY 4 1/4% 2036\",\"NetMoney\":\"2436321.85\",\"ExecType\":\"F\",\"LeavesQty\":\"0\",\"NumDaysInterest\":\"15\",\"AccruedInterestAmt\":\"3447.35\",\"OrderQty2\":\"0\",\"SecondaryOrderID\":\"3739:20130321:50018:5\",\"CouponRate\":\"0.0425\",\"Factor\":\"1\",\"Yield\":\"0.0291371041\",\"Concession\":\"0\",\"GrossTradeAmt\":\"2432874.5\",\"PriceType\":\"1\",\"CountryOfIssue\":\"GB\",\"MaturityDate\":\"20360307\",\"NoPartyIDs\":[{\"PartyIDSource\":\"D\",\"PartyID\":\"VCON\",\"PartyRole\":\"1\",\"NoPartySubIDs\":[{\"PartySubID\":\"14\",\"PartySubIDType\":\"4\"}]},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO:6804469\",\"PartyRole\":\"12\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO\",\"PartyRole\":\"11\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"THINKFOLIO LTD\",\"PartyRole\":\"13\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"SXT\",\"PartyRole\":\"16\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO:6804469\",\"PartyRole\":\"36\"}]},\"Trailer\":{}}"; - Assert.AreEqual(expected, msg.ToJSON(dataDictionary: dd, humanReadableValues: false)); + // CASE 1: params (dd, false) => tags converted to names, enums are not converted + const string expected = "{\"Header\":{\"BeginString\":\"FIX.4.4\",\"BodyLength\":\"638\",\"MsgSeqNum\":\"360\",\"MsgType\":\"8\",\"SenderCompID\":\"BLPTSOX\",\"SendingTime\":\"20130321-15:21:23\",\"TargetCompID\":\"THINKTSOX\",\"TargetSubID\":\"6804469\",\"DeliverToCompID\":\"ZERO\"},\"Body\":{\"AvgPx\":\"122.255\",\"ClOrdID\":\"61101189\",\"CumQty\":\"1990000\",\"Currency\":\"GBP\",\"ExecID\":\"VCON:20130321:50018:5:12\",\"SecurityIDSource\":\"4\",\"LastPx\":\"122.255\",\"LastQty\":\"1990000\",\"OrderID\":\"116\",\"OrderQty\":\"1990000\",\"OrdStatus\":\"2\",\"SecurityID\":\"GB0032452392\",\"Side\":\"1\",\"Symbol\":\"[N/A]\",\"TransactTime\":\"20130321-15:21:23\",\"SettlDate\":\"20130322\",\"TradeDate\":\"20130321\",\"Issuer\":\"UK TSY 4 1/4% 2036\",\"NetMoney\":\"2436321.85\",\"ExecType\":\"F\",\"LeavesQty\":\"0\",\"NumDaysInterest\":\"15\",\"AccruedInterestAmt\":\"3447.35\",\"OrderQty2\":\"0\",\"SecondaryOrderID\":\"3739:20130321:50018:5\",\"CouponRate\":\"0.0425\",\"Factor\":\"1\",\"Yield\":\"0.0291371041\",\"Concession\":\"0\",\"GrossTradeAmt\":\"2432874.5\",\"PriceType\":\"1\",\"CountryOfIssue\":\"GB\",\"MaturityDate\":\"20360307\",\"NoPartyIDs\":[{\"PartyIDSource\":\"D\",\"PartyID\":\"VCON\",\"PartyRole\":\"1\",\"NoPartySubIDs\":[{\"PartySubID\":\"14\",\"PartySubIDType\":\"4\"}]},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO:6804469\",\"PartyRole\":\"12\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO\",\"PartyRole\":\"11\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"THINKFOLIO LTD\",\"PartyRole\":\"13\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"SXT\",\"PartyRole\":\"16\"},{\"PartyIDSource\":\"D\",\"PartyID\":\"TFOLIO:6804469\",\"PartyRole\":\"36\"}]},\"Trailer\":{}}"; + Assert.AreEqual(expected, msg.ToJSON(dataDictionary: dd, convertEnumsToDescriptions: false)); - // emit enums as human-readable strings - StringAssert.Contains("\"MsgType\":\"EXECUTION_REPORT\"", msg.ToJSON(dataDictionary: dd, humanReadableValues: true)); + // CASE 2: params (dd, true) => tags converted to names, enums are converted to names + StringAssert.Contains("\"MsgType\":\"EXECUTION_REPORT\"", msg.ToJSON(dataDictionary: dd, convertEnumsToDescriptions: true)); - // Without a DD: tags aren't translated, and you don't get enums either - StringAssert.Contains("\"35\":\"8\"", msg.ToJSON(dataDictionary: null, humanReadableValues: true)); + // CASE 3: params (null, false) => tags are numbers, enums are not converted + StringAssert.Contains("\"35\":\"8\"", msg.ToJSON(dataDictionary: null)); + + // EXCEPTION CASE: params (null, true) => Exception + var ex = Assert.Throws(delegate { msg.ToJSON(null, true); }); + StringAssert.Contains( + "Must be non-null if 'convertEnumsToDescriptions' is true. (Parameter 'dataDictionary')", + ex.Message); } } }