Index: /cpp/frams/param/paramobj.cpp
===================================================================
--- /cpp/frams/param/paramobj.cpp	(revision 489)
+++ /cpp/frams/param/paramobj.cpp	(revision 490)
@@ -23,6 +23,9 @@
 
 ParamEntry* ParamObject::makeParamTab(ParamInterface *pi, bool stripgroups, bool stripproc,
-				      int firstprop, int maxprops, bool dupentries, int flagsexclude, bool addnew, const char* rename, bool readonly_into_userreadonly)
-{
+				      int firstprop, int maxprops, bool dupentries, int flagsexclude_data, int flagsexclude_tab, bool addnew, const char* rename, bool readonly_into_userreadonly)
+{
+// flagsexclude_data - skip while calculating data offsets
+// flagsexclude_tab - skip while creating paramtab
+// usually _data==_tab, but vmneuron public properties need _data=0 and _tab=PARAM_USERHIDDEN (data object has all fields, paramtab skips private fields)
 	ParamEntry *tab, *t;
 	int i, n, offset;
@@ -30,10 +33,10 @@
 	int count = 0, gcount = 1;
 	if (!stripgroups) gcount = pi->getGroupCount();
-	if (stripproc || flagsexclude)
+	if (stripproc || flagsexclude_tab)
 		for (int i = firstprop; i < pi->getPropCount(); i++)
 		{
 		const char*t = pi->type(i);
 		if ((!stripproc) || (strchr("dfsox", *t)))
-			if ((!flagsexclude) || (!(pi->flags(i)&flagsexclude)))
+			if ((!flagsexclude_tab) || (!(pi->flags(i)&flagsexclude_tab)))
 				if (++count >= maxprops) break;
 		}
@@ -72,16 +75,23 @@
 		if ((!stripproc) || (strchr("dfsox", type[0])))
 		{
-			if ((!flagsexclude) || (!(pi->flags(i)&flagsexclude)))
+			paInt flag=pi->flags(i);
+			int tmp_offset;
+			if ((!flagsexclude_data) || (!(flag&flagsexclude_data)))
 			{
 				if (type[0]=='p')
-					t->offset=0;
+					tmp_offset=0;
 				else
 					{
-					t->offset = offset;
-					if (type[0] != 'x') t->offset += (((char*)&ex.data[0]) - ((char*)&ex));
+					tmp_offset = offset;
+					if (type[0] != 'x') tmp_offset += (((char*)&ex.data[0]) - ((char*)&ex));
 					offset += sizeof(ExtValue);
 					}
+			}
+
+			if ((!flagsexclude_tab) || (!(flag&flagsexclude_tab)))
+			{
+				t->offset=tmp_offset;
 				t->group = (paInt)(stripgroups ? 0 : pi->group(i));
-				t->flags = (paInt)pi->flags(i);
+				t->flags = (paInt)flag;
 				if (readonly_into_userreadonly && (t->flags & PARAM_READONLY))
 					t->flags=(t->flags & ~PARAM_READONLY) | PARAM_USERREADONLY;
@@ -190,5 +200,25 @@
 		case 'f': v->setDouble(0); v++; break;
 		case 's': v->setString(SString::empty()); v++; break;
-		case 'o': v->setObject(ExtObject()); v++; break;
+		case 'o':
+			{
+			ExtObject new_obj;
+			if (t->flags & PARAM_OBJECTSET)
+				{
+				ParamInterface *cls=ExtValue::findDeserializableClass(t->type+1);
+				if (cls)
+					{
+					int new_fun = cls->findId("new");
+					if (new_fun>=0)
+						{
+						ExtValue dummy,new_value;
+						cls->call(new_fun, &dummy, &new_value);
+						new_obj=new_value.getObject();
+						}
+					}
+				}
+			v->setObject(new_obj);
+			v++;
+			}
+			break;
 		case 'x': v++; break;
 	}
Index: /cpp/frams/param/paramobj.h
===================================================================
--- /cpp/frams/param/paramobj.h	(revision 489)
+++ /cpp/frams/param/paramobj.h	(revision 490)
@@ -53,5 +53,5 @@
 		ParamObject::freeObject(obj2);
 		*/
-	static ParamEntry* makeParamTab(ParamInterface *pi, bool stripgroups = 0, bool stripproc = 0, int firstprop = 0, int maxprops = 9999, bool dupentries = false, int flagsexclude = 0, bool addnew = false, const char* rename = NULL,bool readonly_into_userreadonly=false);
+	static ParamEntry* makeParamTab(ParamInterface *pi, bool stripgroups = 0, bool stripproc = 0, int firstprop = 0, int maxprops = 9999, bool dupentries = false, int flagsexclude_data = 0, int flagsexclude_tab = 0, bool addnew = false, const char* rename = NULL,bool readonly_into_userreadonly=false);
 
 	/** deallocate paramtab obtained from makeParamTab() */
Index: /cpp/frams/util/extvalue.cpp
===================================================================
--- /cpp/frams/util/extvalue.cpp	(revision 489)
+++ /cpp/frams/util/extvalue.cpp	(revision 490)
@@ -357,4 +357,11 @@
 }
 
+void ExtValue::setError(const SString& msg)
+{
+ErrorObject *err=new ErrorObject;
+err->message=msg;
+setObject(ErrorObject::makeDynamicObject(err));
+}
+
 static ExtValue::CompareResult longsign(paInt x)
 {
@@ -369,4 +376,11 @@
 	if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
 	return ExtValue::ResultUnequal_RelaxedUnequal; //comparing anything else with null is valid but null is neither higher nor lower than numbers or strings
+}
+
+static ExtValue::CompareResult compareInvalid(const ExtValue& v)
+{
+	if (v.getType()==TInvalid) return ExtValue::ResultEqualUnordered;
+	if ((v.getType() == TInt) && (v.getInt() == 0)) return ExtValue::ResultUnequal_RelaxedEqual;
+	return ExtValue::ResultMismatch; //comparing anything else with invalid is invalid
 }
 
@@ -392,4 +406,8 @@
 	else if (src.isNull())
 		return compareNull(*this);
+	if (getType()==TInvalid)
+		return compareInvalid(src);
+	else if (src.getType()==TInvalid)
+		return compareInvalid(*this);
 	switch (type)
 	{
@@ -1121,17 +1139,22 @@
 		return in + 5;
 	}
-	else if (!strncmp(in, "invalid", 9))
+	else if (!strncmp(in, "invalid", 7))
 	{
 		setInvalid();
-		return in + 9;
+		return in + 7;
 	}
 	else if (*in == '<')
 	{ //unserializable object
-		setInvalid();
-		while (*in)
-			if (*in == '>')
-				return in + 1;
-			else in++;
-			return in;
+		const char* end=in+1;
+		while (*end)
+			if (*end == '>')
+				{
+				setError(SString("Unserializable class: ")+SString(in+1,end-in-1));
+				return end+1;
+				}
+			else
+				end++;
+		logPrintf("ExtValue", "deserialize", LOG_ERROR, "Missing '>'");
+		return NULL;
 	}
 	else if (*in == '^')
@@ -1236,2 +1259,42 @@
 	return v.toString();
 }
+
+#define FIELDSTRUCT ErrorObject
+static ParamEntry errorobject_paramtab[]=
+{
+{"Error",1,3,"Error",},
+{"message",0,0,"Message","s",FIELD(message),},
+{"newFromString",0,0,"create new object","p oError(s message)",PROCEDURE(p_newfromstring),},
+{"toString",0,PARAM_READONLY | PARAM_NOSTATIC,"Textual form","s",GETONLY(toString),},
+{0,0,0,},
+};
+#undef FIELDSTRUCT
+
+Param& ErrorObject::getParam()
+{
+	static Param param(errorobject_paramtab);
+	return param;
+}
+
+ExtObject ErrorObject::makeDynamicObject(ErrorObject* e)
+{
+	return ExtObject(&getParam(), (DestrBase*)e);
+}
+
+const SString ErrorObject::TO_STRING_PREFIX="Error: ";
+
+void ErrorObject::get_toString(ExtValue* ret)
+{
+ret->setString(TO_STRING_PREFIX+message);
+}
+
+void ErrorObject::p_newfromstring(ExtValue *args, ExtValue *ret)
+{
+ErrorObject *err=new ErrorObject();
+err->message=args[0].getString();
+if (err->message.startsWith(TO_STRING_PREFIX.c_str()))
+	err->message=err->message.substr(TO_STRING_PREFIX.len());
+*ret = makeDynamicObject(err);
+}
+
+REGISTER_DESERIALIZABLE(ErrorObject)
Index: /cpp/frams/util/extvalue.h
===================================================================
--- /cpp/frams/util/extvalue.h	(revision 489)
+++ /cpp/frams/util/extvalue.h	(revision 490)
@@ -185,4 +185,5 @@
 	void setEmpty();
 	void setInvalid() { setEmpty(); type = TInvalid; }
+	void setError(const SString& msg);
 	bool makeUnique() { return (type == TObj) && odata().makeUnique(); } //< @return false if nothing has changed
 	ExtPType getType() const { return type; }
@@ -243,3 +244,17 @@
 #define REGISTER_DESERIALIZABLE(name) ExtValue::AddDeserializable<name> deserializable_autoinit_ ## name;
 
-#endif
+class ErrorObject: public DestrBase
+{
+  public:
+SString message;
+static Param& getParam();
+static Param& getStaticParam() {return getParam();}
+static ExtObject makeDynamicObject(ErrorObject* e);
+static const SString TO_STRING_PREFIX;
+#define STATRICKCLASS ErrorObject
+PARAMGETDEF(toString);
+PARAMPROCDEF(p_newfromstring);
+#undef STATRICKCLASS
+};
+
+#endif
Index: /cpp/frams/vm/classes/collectionobj.cpp
===================================================================
--- /cpp/frams/vm/classes/collectionobj.cpp	(revision 489)
+++ /cpp/frams/vm/classes/collectionobj.cpp	(revision 490)
@@ -545,2 +545,6 @@
 	ret->setEmpty();
 }
+
+// not actually needed for deserialization (vector and dict are special cases) but findDeserializableClass can be also used in other contexts
+REGISTER_DESERIALIZABLE(VectorObject)
+REGISTER_DESERIALIZABLE(DictionaryObject)
Index: /cpp/frams/vm/classes/collectionobj.h
===================================================================
--- /cpp/frams/vm/classes/collectionobj.h	(revision 489)
+++ /cpp/frams/vm/classes/collectionobj.h	(revision 490)
@@ -25,4 +25,5 @@
 VectorObject():readonly(0),owndata(1) {}
 ~VectorObject() {clear();}
+static Param& getStaticParam() {return par;}
 #define STATRICKCLASS VectorObject
 PARAMPROCDEF(p_clear) {if (readonly) return; clear();}
@@ -62,4 +63,5 @@
 DictionaryObject():it(hash),it_index(-1) {}
 ~DictionaryObject() {clear();}
+static Param& getStaticParam() {return par;}
 #define STATRICKCLASS DictionaryObject
 PARAMPROCDEF(p_clear) {clear();}
