// ================================================================================================= // ADOBE SYSTEMS INCORPORATED // Copyright 2014 Adobe Systems Incorporated // All Rights Reserved // // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms // of the Adobe license agreement accompanying it. // ================================================================================================= #define IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED 1 #include "XMPCore/ImplHeaders/RDFDOMSerializerImpl.h" #undef IMPLEMENTATION_HEADERS_CAN_BE_INCLUDED #include "XMPCommon/Interfaces/IError_I.h" #include "XMPCore/XMPCoreErrorCodes.h" #include "XMPCommon/Utilities/AutoSharedLock.h" #include "XMPCore/Interfaces/ISimpleNode_I.h" #include "XMPCore/Interfaces/IArrayNode_I.h" #include "XMPCore/Interfaces/IMetadata_I.h" #include "XMPCommon/Interfaces/IUTF8String_I.h" #include "XMPCore/Interfaces/INameSpacePrefixMap_I.h" #include "XMPCommon/Utilities/TSmartPointers_I.h" #include "XMPCommon/Utilities/UTF8String.h" #include "XMPCore/Interfaces/INodeIterator_I.h" #include "XMPMeta.hpp" // some declaration of functions defined in XMPMeta-Parse.cpp extern void NormalizeDCArrays( XMP_Node * xmpTree ); extern void MoveExplicitAliases( XMP_Node * tree, XMP_OptionBits parseOptions, XMPMeta::ErrorCallbackInfo & errorCallback ); extern void TouchUpDataModel( XMPMeta * xmp, XMPMeta::ErrorCallbackInfo & errorCallback ); namespace AdobeXMPCore_Int { namespace Serializer { typedef enum { kCPOmitPacketWrapper, kCPMarkReadOnlyPacket, kCPUseCompactFormat, kCPUseCanonicalFormat, kCPIncludeThumbnailPadding, kCPUseExactPacketLength, kCPOmitAllFormatting, kCPOmitMetaElement, kCPOmitRDFHash, kCPUseEncoding, kCPUseBigEndian, kCPPaddingLength } eConfigurableParameters; static uint64 kAllowedKeys[] = { IConfigurable::ConvertCharBufferToUint64( "oPktWrap" ), IConfigurable::ConvertCharBufferToUint64( "mRoPkt " ), IConfigurable::ConvertCharBufferToUint64( "uCompact" ), IConfigurable::ConvertCharBufferToUint64( "uCanonic" ), IConfigurable::ConvertCharBufferToUint64( "eThmbPad" ), IConfigurable::ConvertCharBufferToUint64( "uExctLen" ), IConfigurable::ConvertCharBufferToUint64( "oFormat " ), IConfigurable::ConvertCharBufferToUint64( "oMetaEl " ), IConfigurable::ConvertCharBufferToUint64( "oRDFHash" ), IConfigurable::ConvertCharBufferToUint64( "encoding" ), IConfigurable::ConvertCharBufferToUint64( "bgEndian"), IConfigurable::ConvertCharBufferToUint64( "padLen " ), }; static ConfigurableImpl::KeyValueTypePair kAllowedKeyValueTypes[] = { std::make_pair( kAllowedKeys[ kCPOmitPacketWrapper ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPMarkReadOnlyPacket ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPUseCompactFormat ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPUseCanonicalFormat ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPIncludeThumbnailPadding ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPUseExactPacketLength ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPOmitAllFormatting ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPOmitMetaElement ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPOmitRDFHash ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPUseEncoding ], IConfigurable::kDTUint64 ), std::make_pair( kAllowedKeys[ kCPUseBigEndian ], IConfigurable::kDTBool ), std::make_pair( kAllowedKeys[ kCPPaddingLength ], IConfigurable::kDTUint64 ) }; } // static utility functions static spcIUTF8String CreateQualifiedName( const spINode & node, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap ) { spIUTF8String qualName = IUTF8String_I::CreateUTF8String( NULL, AdobeXMPCommon::npos ); spcIUTF8String nameSpace = node->GetNameSpace(); static sizet count( 0 ); spcIUTF8String prefixStr = userSuppliedMap->GetPrefix( node->GetNameSpace() ); if ( !prefixStr && !generatedMap ) { generatedMap = MakeUncheckedSharedPointer( INameSpacePrefixMap_I::CreateNameSpacePrefixMap()->GetINameSpacePrefixMap_I(), __FILE__, __LINE__, true ); count = 0; } else if (!prefixStr && generatedMap) { if (generatedMap->GetPrefix(node->GetNameSpace())) { prefixStr = generatedMap->GetPrefix(node->GetNameSpace()); } } if ( !prefixStr ) { spIUTF8String autoGeneratedPrefix = IUTF8String_I::CreateUTF8String( NULL, AdobeXMPCommon::npos ); do { autoGeneratedPrefix->clear(); autoGeneratedPrefix->append( "ns", (sizet) 2 ); //std::string numStr = std::to_string( ++count ); std::ostringstream oss; oss << ++count; std::string numStr = oss.str(); autoGeneratedPrefix->append( numStr.c_str(), numStr.size() ); } while (generatedMap->IsPrefixPresent ( autoGeneratedPrefix->c_str ( ), autoGeneratedPrefix->size ( ) )); generatedMap->Insert( autoGeneratedPrefix->c_str(), autoGeneratedPrefix->size(), nameSpace->c_str(), nameSpace->size() ); prefixStr = autoGeneratedPrefix; } qualName->append( prefixStr ); qualName->append( ":", 1 ); qualName->append( node->GetName() ); return qualName; } bool FindPrefixFromUserSuppliedMap ( void * voidUserSuppliedMap, XMP_StringPtr nsURI, XMP_StringPtr * namespacePrefix, XMP_StringLen * prefixSize ) { if (voidUserSuppliedMap) { pcINameSpacePrefixMap userSuppliedMap = reinterpret_cast( voidUserSuppliedMap ); auto prefix = userSuppliedMap->GetPrefix ( nsURI, AdobeXMPCommon::npos ); if (prefix) { *namespacePrefix = prefix->c_str ( ); *prefixSize = prefix->size ( ); return true; } } return false; } static XMP_Node * AddChildNode( XMP_Node * xmpParent, const spINode & node, const char * value, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel ) { bool isArrayItem = node->IsArrayItem(); if ( isTopLevel ) { isArrayItem = false; } XMP_OptionBits childOptions = 0; spcIUTF8String qualName = CreateQualifiedName( node, userSuppliedMap, generatedMap ); XMP_StringPtr childName = qualName->c_str(); XMP_StringPtr nameSpaceStrPtr = node->GetNameSpace()->c_str(); if ( isTopLevel ) { // Lookup the schema node, adjust the XMP parent pointer. XMP_Assert( xmpParent->parent == 0 ); // Incoming parent must be the tree root. XMP_Node * schemaNode = FindSchemaNode( xmpParent, nameSpaceStrPtr, kXMP_CreateNodes, NULL, &FindPrefixFromUserSuppliedMap, generatedMap ? generatedMap->GetActualINameSpacePrefixMap() : NULL ); if ( schemaNode->options & kXMP_NewImplicitNode ) schemaNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. // *** Should use "opt &= ~flag" (no conditional), need runtime check for proper 32 bit code. xmpParent = schemaNode; // If this is an alias set the isAlias flag in the node and the hasAliases flag in the tree. XMP_StringPtr prefixForAlias( NULL ); XMP_StringLen prefixLenForAlias( 0 ); if ( sRegisteredNamespaces->GetPrefix( nameSpaceStrPtr, &prefixForAlias, &prefixLenForAlias ) && prefixForAlias && prefixLenForAlias > 0 ) { spIUTF8String childNameForAlias( IUTF8String_I::CreateUTF8String( NULL, npos ) ); childNameForAlias->append( prefixForAlias, prefixLenForAlias )->append( node->GetName() ); if ( sRegisteredAliasMap->find( childNameForAlias->c_str() ) != sRegisteredAliasMap->end() ) { childOptions |= kXMP_PropIsAlias; schemaNode->parent->options |= kXMP_PropHasAliases; } } } if ( isArrayItem ) { childName = kXMP_ArrayItemName; } // Add the new child to the XMP parent node. XMP_Node * newChild = new XMP_Node( xmpParent, childName, value, childOptions ); xmpParent->children.push_back( newChild ); return newChild; } static XMP_Node * AddQualifierNode( XMP_Node * xmpParent, const spINode & node, const char * value, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap ) { spcIUTF8String qualName = CreateQualifiedName( node, userSuppliedMap, generatedMap ); XMP_StringPtr childName = qualName->c_str(); static const char * kLanguageName = "lang"; static const char * kTypeName = "type"; const bool isLang = ( node->GetName()->compare( kLanguageName ) == 0 ) && ( node->GetNameSpace()->compare( kXMP_NS_XML ) == 0 ); const bool isType = ( node->GetName()->compare( kTypeName ) == 0 ) && ( node->GetNameSpace()->compare( kXMP_NS_RDF ) == 0 ); bool isArrayItem = node->IsArrayItem(); XMP_OptionBits childOptions = 0; XMP_StringPtr nameSpaceStrPtr = node->GetNameSpace()->c_str(); if ( isArrayItem ) { childName = kXMP_ArrayItemName; } XMP_Node * newQual = 0; newQual = new XMP_Node( xmpParent, childName, value, kXMP_PropIsQualifier ); if ( !( isLang | isType ) ) { xmpParent->qualifiers.push_back( newQual ); } else if ( isLang ) { if ( xmpParent->qualifiers.empty() ) { xmpParent->qualifiers.push_back( newQual ); } else { xmpParent->qualifiers.insert( xmpParent->qualifiers.begin(), newQual ); } xmpParent->options |= kXMP_PropHasLang; } else { XMP_Assert( isType ); if ( xmpParent->qualifiers.empty() ) { xmpParent->qualifiers.push_back( newQual ); } else { size_t offset = 0; if ( XMP_PropHasLang( xmpParent->options ) ) offset = 1; xmpParent->qualifiers.insert( xmpParent->qualifiers.begin() + offset, newQual ); } xmpParent->options |= kXMP_PropHasType; } xmpParent->options |= kXMP_PropHasQualifiers; return newQual; } void HandleNode( const spINode & node, XMP_Node * parent, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel, bool isQualifierNode ); XMP_Node * HandleSimpleNode( const spISimpleNode & simpleNode, XMP_Node * parent, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel, bool isQualifierNode ) { XMP_Node * node = NULL; if ( isQualifierNode ) { node = AddQualifierNode( parent, simpleNode, simpleNode->GetValue()->c_str(), userSuppliedMap, generatedMap ); } else { node = AddChildNode( parent, simpleNode, simpleNode->GetValue()->c_str(), userSuppliedMap, generatedMap, isTopLevel ); } if ( simpleNode->IsURIType() ) node->options |= kXMP_PropValueIsURI; return node; } XMP_Node * HandleStructureNode( const spIStructureNode & structureNode, XMP_Node * parent, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel, bool isQualifierNode ) { bool metadataNode = false; if ( isTopLevel ) { // check if it is a XMPMetadata node spIMetadata metadata = structureNode->ConvertToMetadata(); if ( metadata ) { metadataNode = true; parent->name = metadata->GetAboutURI()->c_str(); } } XMP_Node * newComposite = NULL; if ( !metadataNode ) { if ( isQualifierNode ) { newComposite = AddQualifierNode( parent, structureNode, "", userSuppliedMap, generatedMap ); } else { newComposite = AddChildNode( parent, structureNode, "", userSuppliedMap, generatedMap, isTopLevel ); } } else { newComposite = parent; } if ( newComposite == 0 ) return NULL; // Ignore lower level errors. newComposite->options |= kXMP_PropValueIsStruct; // iterate over all the children spINodeIterator it = structureNode->Iterator(); while ( it ) { spINode spNode = it->GetNode(); if ( metadataNode ) HandleNode( spNode, newComposite, userSuppliedMap, generatedMap, true, false ); else HandleNode( spNode, newComposite, userSuppliedMap, generatedMap, false, false ); it = it->Next(); } return newComposite; } XMP_Node * HandleArrayNode( const spIArrayNode & arrayNode, XMP_Node * parent, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel, bool isQualifierNode ) { XMP_Node * newComposite = NULL; if ( isQualifierNode ) { newComposite = AddQualifierNode( parent, arrayNode, "", userSuppliedMap, generatedMap ); } else { newComposite = AddChildNode( parent, arrayNode, "", userSuppliedMap, generatedMap, isTopLevel ); } if ( newComposite == 0 ) return NULL; // Ignore lower level errors. newComposite->options |= kXMP_PropValueIsArray; IArrayNode::eArrayForm arrayNodeForm = arrayNode->GetArrayForm(); if ( arrayNodeForm == IArrayNode::kAFAlternative ) newComposite->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate; else if ( arrayNodeForm == IArrayNode::kAFOrdered ) newComposite->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered; // iterate over all the children spINodeIterator it = arrayNode->Iterator(); while ( it ) { spINode spNode = it->GetNode(); HandleNode( spNode, newComposite, userSuppliedMap, generatedMap, false, false ); it = it->Next(); } return newComposite; } void HandleNode( const spINode & node, XMP_Node * parent, const spcINameSpacePrefixMap_I & userSuppliedMap, spINameSpacePrefixMap_I & generatedMap, bool isTopLevel, bool isQualifierNode ) { XMP_Node * nodeCreated = NULL; if (!node) return; switch ( node->GetNodeType() ) { case INode::kNTSimple: nodeCreated = HandleSimpleNode( node->ConvertToSimpleNode(), parent, userSuppliedMap, generatedMap, isTopLevel, isQualifierNode ); break; case INode::kNTStructure: nodeCreated = HandleStructureNode( node->ConvertToStructureNode(), parent, userSuppliedMap, generatedMap, isTopLevel, isQualifierNode ); break; case INode::kNTArray: nodeCreated = HandleArrayNode( node->ConvertToArrayNode(), parent, userSuppliedMap, generatedMap, isTopLevel, isQualifierNode ); break; default: break; } if ( node->HasQualifiers() && nodeCreated ) { spINodeIterator it = node->QualifiersIterator(); while ( it ) { spINode spNode = it->GetNode(); HandleNode( spNode, nodeCreated, userSuppliedMap, generatedMap, false, true ); it = it->Next(); } } } DOMSerializerImpl * APICALL RDFDOMSerializerImpl::clone() const { return new RDFDOMSerializerImpl(); } static void GetSerializationOptions( pcIConfigurable configurationParameters, XMP_OptionBits & options, uint64 & paddingSize ) { options = 0; bool flag; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitPacketWrapper ], flag ) && flag ) options |= kXMP_OmitPacketWrapper; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPMarkReadOnlyPacket ], flag ) && flag ) options |= kXMP_ReadOnlyPacket; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseCompactFormat ], flag ) && flag ) options |= kXMP_UseCompactFormat; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseCanonicalFormat ], flag ) && flag ) options |= kXMP_UseCanonicalFormat; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPIncludeThumbnailPadding ], flag ) && flag ) options |= kXMP_IncludeThumbnailPad; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseExactPacketLength ], flag ) && flag ) options |= kXMP_ExactPacketLength; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitAllFormatting ], flag ) && flag ) options |= kXMP_OmitAllFormatting; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitMetaElement ], flag ) && flag ) options |= kXMP_OmitXMPMetaElement; if ( configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitRDFHash ], flag ) && !flag ) options |= kXMP_IncludeRDFHash; uint64 encoding( 8 ); if ( !configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseBigEndian ], flag ) ) flag = false; if ( !configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseEncoding ], encoding ) ) encoding = 8; switch ( encoding ) { case 16: if ( flag ) options |= kXMP_EncodeUTF16Big; else options |= kXMP_EncodeUTF16Little; break; case 32: if ( flag ) options |= kXMP_EncodeUTF32Big; else options |= kXMP_EncodeUTF32Little; break; default: options |= kXMP_EncodeUTF8; } if ( !configurationParameters->GetParameter( Serializer::kAllowedKeys[ Serializer::kCPPaddingLength ], paddingSize ) ) paddingSize = 2048; } spIUTF8String APICALL RDFDOMSerializerImpl::Serialize( const spINode & node, const spcINameSpacePrefixMap & nameSpacePrefixMap ) { XMP_OptionBits options = 0; shared_ptr< XMPMeta > spMeta( new XMPMeta() ); spINameSpacePrefixMap_I genereatedMap; spcINameSpacePrefixMap mergedMap = INameSpacePrefixMap::GetDefaultNameSpacePrefixMap(); if ( nameSpacePrefixMap ) { spINameSpacePrefixMap newMergedMap = mergedMap->Clone(); newMergedMap->GetINameSpacePrefixMap_I()->Merge( nameSpacePrefixMap ); mergedMap = newMergedMap; } spINameSpacePrefixMap_I userSuppliedMap( MakeUncheckedSharedPointer( const_pointer_cast< INameSpacePrefixMap >( mergedMap )->GetINameSpacePrefixMap_I(), __FILE__, __LINE__, true ) ); // TODO:meta->SetErrorCallback() HandleNode( node, &spMeta->tree, userSuppliedMap, genereatedMap, true, false ); NormalizeDCArrays( &( spMeta->tree ) ); if ( spMeta->tree.options & kXMP_PropHasAliases ) MoveExplicitAliases( &spMeta->tree, options, spMeta->errorCallback ); TouchUpDataModel( spMeta.get(), spMeta->errorCallback ); // Delete empty schema nodes. Do this last, other cleanup can make empty schema. size_t schemaNum = 0; while ( schemaNum < spMeta->tree.children.size() ) { XMP_Node * currSchema = spMeta->tree.children[ schemaNum ]; if ( currSchema->children.size() > 0 ) { ++schemaNum; } else { delete spMeta->tree.children[ schemaNum ]; // ! Delete the schema node itself. spMeta->tree.children.erase( spMeta->tree.children.begin() + schemaNum ); } } std::string buffer; uint64 padding; GetSerializationOptions( this, options, padding ); spMeta->SerializeToBuffer( &buffer, options, padding, "", "", 0 ); spIUTF8String serializedOutput = IUTF8String_I::CreateUTF8String( buffer.c_str(), buffer.size() ); return serializedOutput; } eConfigurableErrorCode APICALL RDFDOMSerializerImpl::ValidateValue( const uint64 & key, eDataType type, const CombinedDataValue & value ) const { if ( key == Serializer::kAllowedKeys[ Serializer::kCPUseEncoding ] ) { if ( type == IConfigurable::kDTUint64 && ( value.uint64Value == 8 || value.uint64Value == 16 || value.uint64Value == 32 ) ) return kCECNone; return kCECValueNotSupported; } return kCECNone; } void RDFDOMSerializerImpl::InitializeDefaultValues() { TreatKeyAsCaseInsensitive( true ); AllowDifferentValueTypesForExistingEntries( false ); SetAllowedKeys( &Serializer::kAllowedKeys[ 0 ], 12 ); SetAllowedValueTypesForKeys( &Serializer::kAllowedKeyValueTypes[ 0 ], 12 ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitPacketWrapper ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPMarkReadOnlyPacket ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseCompactFormat ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseCanonicalFormat ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPIncludeThumbnailPadding ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseExactPacketLength ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitAllFormatting ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitMetaElement ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPOmitRDFHash ], true ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseEncoding ], ( uint64 ) 8 ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPUseBigEndian ], false ); SetParameter( Serializer::kAllowedKeys[ Serializer::kCPPaddingLength ], ( uint64 ) 2048 ); } spIUTF8String APICALL RDFDOMSerializerImpl::SerializeInternal(const spINode & node, XMP_OptionBits options, sizet padding, const char * newline, const char * indent, sizet baseIndent, const spcINameSpacePrefixMap & nameSpacePrefixMap) const { shared_ptr< XMPMeta > spMeta(new XMPMeta()); spINameSpacePrefixMap_I genereatedMap; spcINameSpacePrefixMap mergedMap = INameSpacePrefixMap::GetDefaultNameSpacePrefixMap(); if (nameSpacePrefixMap) { spINameSpacePrefixMap newMergedMap = mergedMap->Clone(); newMergedMap->GetINameSpacePrefixMap_I()->Merge(nameSpacePrefixMap); mergedMap = newMergedMap; } spINameSpacePrefixMap_I userSuppliedMap(MakeUncheckedSharedPointer(const_pointer_cast(mergedMap)->GetINameSpacePrefixMap_I(), __FILE__, __LINE__, true)); // TODO:meta->SetErrorCallback() HandleNode(node, &spMeta->tree, userSuppliedMap, genereatedMap, true, false); NormalizeDCArrays(&(spMeta->tree)); if (spMeta->tree.options & kXMP_PropHasAliases) MoveExplicitAliases(&spMeta->tree, options, spMeta->errorCallback); TouchUpDataModel(spMeta.get(), spMeta->errorCallback); // Delete empty schema nodes. Do this last, other cleanup can make empty schema. size_t schemaNum = 0; while (schemaNum < spMeta->tree.children.size()) { XMP_Node * currSchema = spMeta->tree.children[schemaNum]; if (currSchema->children.size() > 0) { ++schemaNum; } else { delete spMeta->tree.children[schemaNum]; // ! Delete the schema node itself. spMeta->tree.children.erase(spMeta->tree.children.begin() + schemaNum); } } std::string buffer; spMeta->SerializeToBuffer(&buffer, options, padding, newline, indent, baseIndent); spIUTF8String serializedOutput = IUTF8String_I::CreateUTF8String(buffer.c_str(), buffer.size()); return serializedOutput; } }