tamlJSONWriter.cpp
Engine/source/persistence/taml/json/tamlJSONWriter.cpp
Public Variables
Detailed Description
Public Variables
StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS
RapidJson.
StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2013 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "persistence/taml/json/tamlJSONWriter.h" 25 26#include "core/stringTable.h" 27#include "core/stream/fileStream.h" 28 29// Debug Profiling. 30#include "platform/profiler.h" 31 32//----------------------------------------------------------------------------- 33 34// These are the characters allowed that separate the type-name from the type. 35// These separators can be used to ensure that each member in an object has 36// a unique name. 37// 38// It is important to understand that TAML does not require entries to be unique 39// but technically the RFC4627 spec states it as "should" be seems to be taken 40// as MUST. See here: http://www.ietf.org/rfc/rfc4627.txt 41// 42// They can be placed as a suffix to the type-name. The very first occurance 43// is used to split the type-name from the suffix so you can form whatever 44// suffix you like to make entries unique. 45// Examples are "Sprite 0", "Sprite*0", "Sprite[0]", "Sprite,0" etc. 46// 47// Type-names can legally consist of a-z, A-Z, 0-9 and "_" (underscore) so these 48// characters cannot be used for mangling. Feel free to add any characters you 49// require to this list. 50StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS = StringTable->insert(" !$%^&*()-+{}[]@:~#|\\/?<>,.\n\r\t"); 51 52// This is the "dSprintf" format that the JSON writer uses to encode each 53// member if JSON_RFC4627 mode is on. You are free to change this as long as 54// you ensure that it starts with the "%s" character (which represents the type name) 55// and is immediately followed by at least a single mangling character and that the 56// "%d" is present as that represents the automatically-added member index. 57StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT = StringTable->insert( "%s[%d]" ); 58 59//----------------------------------------------------------------------------- 60 61bool TamlJSONWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode ) 62{ 63 // Debug Profiling. 64 PROFILE_SCOPE(TamlJSONWriter_Write); 65 66 // Create document. 67 rapidjson::Document document; 68 document.SetObject(); 69 70 // Compile the root type. 71 rapidjson::Value rootValue(rapidjson::kObjectType); 72 compileType( document, &rootValue, NULL, pTamlWriteNode, -1 ); 73 74 // Write document to stream. 75 rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream ); 76 document.Accept( jsonStreamWriter ); 77 78 return true; 79} 80 81//----------------------------------------------------------------------------- 82 83void TamlJSONWriter::compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex ) 84{ 85 // Debug Profiling. 86 PROFILE_SCOPE(TamlJSONWriter_CompileType); 87 88 // Fetch the json document allocator. 89 rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); 90 91 // Fetch object. 92 SimObject* pSimObject = pTamlWriteNode->mpSimObject; 93 94 // Fetch JSON strict flag (don't use it if member index is set to not use it). 95 const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict(); 96 97 // Fetch element name (mangled or not). 98 StringTableEntry elementName = jsonStrict ? getManagedName( pSimObject->getClassName(), memberIndex ) : pSimObject->getClassName(); 99 100 // Is there a parent value? 101 if ( pParentValue == NULL ) 102 { 103 // No, so add as document root value member. 104 pTypeValue = &((document.AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value); 105 } 106 else 107 { 108 // Yes, so add as a parent value member. 109 pTypeValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value); 110 } 111 112 // Fetch reference Id. 113 const U32 referenceId = pTamlWriteNode->mRefId; 114 115 // Do we have a reference Id? 116 if ( referenceId != 0 ) 117 { 118 // Yes, so set reference Id. 119 rapidjson::Value value; 120 value.SetInt( referenceId ); 121 pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefIdName), value, allocator ); 122 } 123 // Do we have a reference to node? 124 else if ( pTamlWriteNode->mRefToNode != NULL ) 125 { 126 // Yes, so fetch reference to Id. 127 const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId; 128 129 // Sanity! 130 AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." ); 131 132 // Set reference to Id. 133 rapidjson::Value value; 134 value.SetInt( referenceToId ); 135 pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefToIdName), value, allocator ); 136 137 // Finish because we're a reference to another object. 138 return; 139 } 140 141 // Fetch object name. 142 const char* pObjectName = pTamlWriteNode->mpObjectName; 143 144 // Do we have a name? 145 if ( pObjectName != NULL ) 146 { 147 // Yes, so set name. 148 rapidjson::Value value; 149 value.SetString( pObjectName, dStrlen(pObjectName), allocator ); 150 pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlNamedObjectName), value, allocator ); 151 } 152 153 // Compile field. 154 compileFields( document, pTypeValue, pTamlWriteNode ); 155 156 // Fetch children. 157 Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren; 158 159 // Do we have any children? 160 if ( pChildren ) 161 { 162 S32 childMemberIndex = 0; 163 164 // Yes, so iterate children. 165 for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr ) 166 { 167 // Compile child type. 168 rapidjson::Value childValue(rapidjson::kObjectType); 169 compileType( document, &childValue, pTypeValue, (*itr), childMemberIndex++ ); 170 } 171 } 172 173 // Compile custom. 174 compileCustom( document, pTypeValue, pTamlWriteNode ); 175} 176 177//----------------------------------------------------------------------------- 178 179void TamlJSONWriter::compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode ) 180{ 181 // Debug Profiling. 182 PROFILE_SCOPE(TamlJSONWriter_CompileFields); 183 184 // Fetch fields. 185 const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields; 186 187 // Ignore if no fields. 188 if ( fields.size() == 0 ) 189 return; 190 191 // Fetch the json document allocator. 192 rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); 193 194 // Iterate fields. 195 for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr ) 196 { 197 // Fetch field/value pair. 198 TamlWriteNode::FieldValuePair* pFieldValue = (*itr); 199 200 // Set field attribute. 201 rapidjson::Value value; 202 value.SetString( pFieldValue->mpValue, dStrlen(pFieldValue->mpValue), allocator ); 203 pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(pFieldValue->mName), value, allocator ); 204 } 205} 206 207//----------------------------------------------------------------------------- 208 209void TamlJSONWriter::compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode ) 210{ 211 // Debug Profiling. 212 PROFILE_SCOPE(TamlJSONWriter_CompileCustom); 213 214 // Fetch custom nodes. 215 const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes; 216 217 // Fetch custom nodes. 218 const TamlCustomNodeVector& nodes = customNodes.getNodes(); 219 220 // Finish if no custom nodes to process. 221 if ( nodes.size() == 0 ) 222 return; 223 224 // Fetch the json document allocator. 225 rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); 226 227 // Fetch object. 228 SimObject* pSimObject = pTamlWriteNode->mpSimObject; 229 230 // Fetch element name. 231 const char* pElementName = pSimObject->getClassName(); 232 233 // Iterate custom nodes. 234 for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr ) 235 { 236 // Fetch the custom node. 237 TamlCustomNode* pCustomNode = *customNodesItr; 238 239 // Format extended element name. 240 char extendedElementNameBuffer[256]; 241 dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pElementName, pCustomNode->getNodeName() ); 242 StringTableEntry elementNameEntry = StringTable->insert( extendedElementNameBuffer ); 243 244 rapidjson::Value elementValue(rapidjson::kObjectType); 245 rapidjson::Value* pElementValue = &((pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementNameEntry), elementValue, allocator ).MemberEnd()-1)->value); 246 247 // Fetch node children. 248 const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren(); 249 250 S32 childMemberIndex = 0; 251 252 // Iterate children nodes. 253 for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr ) 254 { 255 // Fetch child node. 256 const TamlCustomNode* pChildNode = *childNodeItr; 257 258 // Compile the custom node. 259 compileCustomNode( document, pElementValue, pChildNode, childMemberIndex++ ); 260 } 261 262 // Finish if the node is set to ignore if empty and it is empty. 263 if ( pCustomNode->getIgnoreEmpty() && pTypeValue->MemberBegin() == pTypeValue->MemberEnd() ) 264 { 265 // Yes, so delete the member. 266 pElementValue->SetNull(); 267 } 268 } 269} 270 271//----------------------------------------------------------------------------- 272 273void TamlJSONWriter::compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex ) 274{ 275 // Debug Profiling. 276 PROFILE_SCOPE(TamlJSONWriter_CompileCustomNode); 277 278 // Finish if the node is set to ignore if empty and it is empty. 279 if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() ) 280 return; 281 282 // Is the node a proxy object? 283 if ( pCustomNode->isProxyObject() ) 284 { 285 // Yes, so write the proxy object. 286 rapidjson::Value proxyValue(rapidjson::kObjectType); 287 compileType( document, &proxyValue, pParentValue, pCustomNode->getProxyWriteNode(), memberIndex ); 288 return; 289 } 290 291 // Fetch the json document allocator. 292 rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); 293 294 // Fetch fields. 295 const TamlCustomFieldVector& fields = pCustomNode->getFields(); 296 297 // Fetch JSON strict flag (don't use it if member index is set to not use it). 298 const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict(); 299 300 // Fetch element name (mangled or not). 301 StringTableEntry nodeName = jsonStrict ? getManagedName( pCustomNode->getNodeName(), memberIndex ) : pCustomNode->getNodeName(); 302 303 // Is there any node text? 304 if ( !pCustomNode->getNodeTextField().isValueEmpty() ) 305 { 306 // Yes, so fetch text. 307 const char* pNodeText = pCustomNode->getNodeTextField().getFieldValue(); 308 309 // Create custom value. 310 rapidjson::Value customTextValue(rapidjson::kArrayType); 311 customTextValue.PushBack(rapidjson::GenericStringRef<UTF8>(pNodeText), allocator ); 312 pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customTextValue, allocator ); 313 return; 314 } 315 316 // Create custom value. 317 rapidjson::Value customValue(rapidjson::kObjectType); 318 rapidjson::Value* pCustomValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customValue, allocator ).MemberEnd()-1)->value); 319 320 // Iterate fields. 321 for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr ) 322 { 323 // Fetch field. 324 const TamlCustomField* pField = *fieldItr; 325 326 // Add a field. 327 rapidjson::Value fieldValue; 328 fieldValue.SetString( pField->getFieldValue(), dStrlen(pField->getFieldValue()), allocator ); 329 pCustomValue->AddMember(rapidjson::GenericStringRef<UTF8>(pField->getFieldName()), fieldValue, allocator ); 330 } 331 332 // Fetch node children. 333 const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren(); 334 335 S32 childMemberIndex = 0; 336 337 // Iterate children nodes. 338 for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr ) 339 { 340 // Fetch child node. 341 const TamlCustomNode* pChildNode = *childNodeItr; 342 343 // Compile the child node. 344 compileCustomNode( document, pCustomValue, pChildNode, childMemberIndex++ ); 345 } 346 347 // Finish if the node is set to ignore if empty and it is empty (including fields). 348 if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pCustomValue->MemberBegin() == pCustomValue->MemberEnd() ) 349 { 350 // Yes, so delete the member. 351 pCustomValue->SetNull(); 352 } 353} 354 355//----------------------------------------------------------------------------- 356 357inline StringTableEntry TamlJSONWriter::getManagedName( const char* pName, const S32 memberIndex ) 358{ 359 // Debug Profiling. 360 PROFILE_SCOPE(TamlJSONWriter_getManagedName); 361 362 char nameBuffer[1024]; 363 364 // Format mangled name. 365 dSprintf( nameBuffer, sizeof(nameBuffer), JSON_RFC4627_NAME_MANGLING_FORMAT, pName, memberIndex ); 366 367 return StringTable->insert( nameBuffer ); 368} 369