MIRA
JSON2Python.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Contact: info@mira-project.org
9  *
10  * Commercial Usage:
11  * Licensees holding valid commercial licenses may use this file in
12  * accordance with the commercial license agreement provided with the
13  * software or, alternatively, in accordance with the terms contained in
14  * a written agreement between you and MLAB or NICR.
15  *
16  * GNU General Public License Usage:
17  * Alternatively, this file may be used under the terms of the GNU
18  * General Public License version 3.0 as published by the Free Software
19  * Foundation and appearing in the file LICENSE.GPL3 included in the
20  * packaging of this file. Please review the following information to
21  * ensure the GNU General Public License version 3.0 requirements will be
22  * met: http://www.gnu.org/copyleft/gpl.html.
23  * Alternatively you may (at your option) use any later version of the GNU
24  * General Public License if such license has been publicly approved by
25  * MLAB and NICR (or its successors, if any).
26  *
27  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
28  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
29  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
30  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
33  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
35  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
36  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
37  */
38 
47 #ifndef _MIRA_TOOLBOXES_PYTHON_JSON2PYTHON_H_
48 #define _MIRA_TOOLBOXES_PYTHON_JSON2PYTHON_H_
49 
51 
52 #include <utils/Foreach.h>
53 #include <utils/Time.h>
54 #include <json/JSON.h>
55 
56 namespace mira { namespace python {
57 
59 
60 json::Value to_json(boost::python::object o)
61 {
62  try
63  {
64  if ( o.ptr() == Py_None )
65  return json::Value();
66  else if ( o.ptr() == Py_False )
67  return json::Value(false);
68  else if ( o.ptr() == Py_True )
69  return json::Value(true);
70  else if ( boost::python::extract<int64>(o).check() )
71  return json::Value(boost::python::extract<int64>(o)());
72  else if ( boost::python::extract<double>(o).check() )
73  return json::Value(boost::python::extract<double>(o)());
74  else if (boost::python::extract<boost::python::list>(o).check()
75  || boost::python::extract< boost::python::tuple >(o).check()
76  || PyAnySet_Check(o.ptr()))
77  {
78  if ( PyAnySet_Check(o.ptr()) )
79  o = boost::python::list(o);
80  std::size_t len = boost::python::len(o);
81  json::Array array;
82  array.reserve(len);
83  for (std::size_t p = 0; p != len; ++p)
84  array.push_back(to_json(o[p]));
85  return array;
86  } else if (boost::python::extract<boost::python::dict>(o).check())
87  {
88  json::Object object;
89  // in Python 3 dict.keys() returns an iterable/view and not a list
90  boost::python::object keys = boost::python::list(o.attr("keys")());
91  // TODO: Find out why this is done backwards
92  for (std::size_t len = boost::python::len( keys ); len > 0; --len)
93  {
94  boost::python::object key = keys[len - 1];
95  std::string keystr;
96  // need to check for keys in unicode format
97  if (PyType_IsSubtype((PyTypeObject*)key.ptr()->ob_type, &PyUnicode_Type)) {
98  boost::python::str strFromUnicode(key);
99  strFromUnicode.encode("utf-8");
100  keystr = boost::python::extract<std::string>(strFromUnicode)();
101  } else
102  keystr = boost::python::extract<std::string>(key)();
103 
104  object[keystr] = boost::python::extract<json::Value>(o[key])();
105  }
106  return object;
107  } else if (boost::python::extract<mira::Time>(o).check()) {
108  auto time = boost::python::extract<mira::Time>(o)();
109  return json::Value(time.toUnixNS());
110  } else if (boost::python::extract<std::string>(o).check())
111  return json::Value(boost::python::extract<std::string>(o)());
112  else if (boost::python::extract<json::Object>(o).check())
113  return boost::python::extract<json::Object>(o)();
114  else if (boost::python::extract<json::Array>(o).check())
115  return boost::python::extract<json::Array>(o)();
116  else {
117  // need an explicit type check for PyUnicode here as the str() conversion might work
118  // for types we do not actually want to handle in this way
119  if (PyType_IsSubtype((PyTypeObject*)o.ptr()->ob_type, &PyUnicode_Type)) {
120  boost::python::str strFromUnicode(o);
121  strFromUnicode.encode("utf-8");
122  if (boost::python::extract<std::string>(strFromUnicode).check())
123  return json::Value(boost::python::extract<std::string>(strFromUnicode)());
124  }
125  }
126 
127  // just throw if we did not return yet
128  MIRA_THROW(XNotImplemented, "python::to_json(boost::python::object) got object of unsupported type");
129 
130  } catch (Exception& e)
131  {
132  if (o.ptr() && o.ptr()->ob_type && o.ptr()->ob_type->tp_name)
133  MIRA_RETHROW(e, "Whilst converting a Python object of type "
134  << o.ptr()->ob_type->tp_name << " to a json::Value");
135  MIRA_RETHROW(e, "Whilst converting a Python object of unknown type to a json::Value");
136  }
137 }
138 
139 boost::python::object from_json(const json::Value& j)
140 {
141  try
142  {
143  if ( j.is_null() )
144  return boost::python::object();
145  else if (j.type() == json_spirit::bool_type)
146  return boost::python::object(j.get_bool());
147  else if (j.type() == json_spirit::int_type)
148  return boost::python::object(j.get_int64());
149  else if (j.type() == json_spirit::real_type)
150  return boost::python::object(j.get_real());
151  else if (j.type() == json_spirit::str_type)
152  return boost::python::object(j.get_str());
153  else if (j.type() == json_spirit::array_type) {
154  boost::python::list l;
155  foreach(auto e, j.get_array())
156  l.append(from_json(e));
157  return l;
158  }
159  else if (j.type() == json_spirit::obj_type) {
160  return boost::python::object(j.get_obj());
161  }
162  else
163  MIRA_THROW(XNotImplemented, "python::from_json(const json::Value&) got json value of unsupported type");
164  } catch (Exception& e)
165  {
166  MIRA_RETHROW(e, "Whilst converting a json:Value to its equivalent Python type\n"
167  << json::write(j));
168  }
169 }
170 
172 
173 }} // namespace
174 
175 #endif
void write(const Value &value, std::ostream &ioStream, bool formatted=false, int precision=-1)
json_spirit::mArray Array
#define MIRA_RETHROW(ex, msg)
#define MIRA_THROW(ex, msg)
json_spirit::mObject Object
boost::python::object from_json(const json::Value &j)
Definition: JSON2Python.h:139
json_spirit::mValue Value
json::Value to_json(boost::python::object o)
Definition: JSON2Python.h:60
Include this instead of boost/python.hpp to reduce compile time warning spam from Boost internal inco...