15

I want to convert viewModel to Json object. But I don't want to map computed properties.

Oguz Karadenizli
  • 3,449
  • 6
  • 38
  • 73

2 Answers2

37

Here are a few options, if you are going to convert it to JSON:

  1. if you are using constructor functions for your object, then you can override the .toJSON function to control which properties to output. Here is an article on it: http://www.knockmeout.net/2011/04/controlling-how-object-is-converted-to.html. Here is a sample: http://jsfiddle.net/rniemeyer/FE4HX/.

  2. in KO 2.1, when using ko.toJSON the second and third arguments are now passed to JSON.stringify. Here is some documentation on the arguments: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify. This means that you can pass the second argument (replacer) with either an array of properties to include or a function that processes the key/values. Here is the same sample using this technique: http://jsfiddle.net/rniemeyer/huyLe/.

  3. Another option that I use frequently, is to define computeds that you don't want in your JSON output as sub-observables. Observables are functions, which are objects, so you can actually define observables on observables. Like:

-

this.name = ko.observable("Bob");
this.name.formatted = ko.computed(...);

Now when converting to JSON, formatted will be naturally lost as name gets converted to its value. Here is the same sample again: http://jsfiddle.net/rniemeyer/peEGG/. Usually I use this when it is meta-data about an observable (isValid, isEditing, etc.).

machineghost
  • 33,529
  • 30
  • 159
  • 234
RP Niemeyer
  • 114,592
  • 18
  • 291
  • 211
  • 3
    That third option saved my ass. I love you, Niemeyer. – SoreThumb Dec 30 '12 at 00:01
  • Similar to José Angel Yánez answer below, here's a way to use a replacer method like in 2nd option above, but to create an JSON object (like ko.toJS): ko.toJS2 = function (model, replacer) { return JSON.parse(ko.toJSON(model, replacer)); } – David Russell Nov 04 '13 at 11:57
  • 1
    Wish I knew the third one earlier! Thank you, Niemeyer. – Maximilian Lindsey Feb 26 '14 at 15:47
  • let me add my name to the list of those loving the sub-observable. thanks very many. – Dave Rael Aug 03 '14 at 19:57
  • Third option should be the first one, till now I didn't think about it even though I am using validation plugin which uses same approach for isValid function – Nish Mar 25 '15 at 12:41
  • Lol @Niemeyer, the third option saved me.. don't know how in the world you came up with the fact that the mapping plugin would ignore that sub-observable but thanks! – Benjamin Vison Jan 25 '16 at 17:55
  • 2
    I know we're not supposed to tack on with "me too" comments... but option #3 is so slick that I spontaneously just said to my coworkers that I "heart" Ryan Niemeyer. – Nate Jackson Apr 07 '16 at 16:30
  • the #3 is really witty, but quite ugly in terms of design I think customizing the serializer is a better approach. (but I need to admit it's tempting) – mikus Feb 14 '17 at 17:13
7

This will also work, it will just ignore anything with a 'mappedProperties' in it, for duck-type naysayers remember you shouldn't have mappedProperties as part of your code since you're using knockout. Therefore it should work.

/* Use this serializer function along with ko.toJS to produce clean JSON objects. */
ko.toJS2 = function (model)
{
     return JSON.parse(ko.toJSON(model, modelSerializer));
}

function modelSerializer(key, value)
{
if (isSerializable(value))
    return value;
else
    return;
}

function isSerializable(object) {
    if (object == null) return true;
    if (typeof object == 'function') return false;
    if (object.mappedProperties != null) return false;

    return true;
}

Usage:

var goodLookingJson = ko.toJS2(whateverModel);
José Yánez
  • 161
  • 1
  • 2
  • Thanks, hat-tip for posting code! Just to clarify, your `ko.toJS2` function returns pure javascript objects, not JSON strings, right? Would you just strip out the JSON.parse call to create a `ko.toJSON2` (or `ko.toCleanJSON` as I'd call it)? – aaaidan Jul 21 '13 at 03:53