class Merger(object): STRATEGIES = { "discard": strategies.Discard(), "overwrite": strategies.Overwrite(), "version": strategies.Version(), "append": strategies.Append(), "objectMerge": strategies.ObjectMerge(), "arrayMergeById": strategies.ArrayMergeById() } def __init__(self, schema, strategies=(), objclass_def='dict', objclass_menu=None, validatorclass=Draft4Validator): """Create a new Merger object. schema -- JSON schema to use when merging. strategies -- Any additional merge strategies to use during merge. objclass_def -- Name of the default class for JSON objects. objclass_menu -- Any additional classes for JSON objects. validatorclass -- JSON Schema validator class. strategies argument should be a dict mapping strategy names to instances of Strategy subclasses. objclass_def specifies the default class used for JSON objects when one is not specified in the schema. It should be 'dict' (dict built-in), 'OrderedDict' (collections.OrderedDict) or one of the names specified in the objclass_menu argument. If not specified, 'dict' is used. objclass_menu argument should be a dictionary that maps a string name to a function or class that will return an empty dictionary-like object to use as a JSON object. The function must accept either no arguments or a dictionary-like object. validatorclass argument can be used to supply a validator class from jsonschema. This can be used for example to specify which JSON Schema draft version will be used during merge. """ self.schema = schema if hasattr(validatorclass, 'ID_OF'): resolver = LocalRefResolver.from_schema(schema, id_of=validatorclass.ID_OF) else: # jsonschema<3.0.0 resolver = LocalRefResolver.from_schema(schema) self.validator = validatorclass(schema, resolver=resolver) self.strategies = dict(self.STRATEGIES) self.strategies.update(strategies) self.objclass_menu = {'dict': dict, 'OrderedDict': OrderedDict} if objclass_menu: self.objclass_menu.update(objclass_menu) self.objclass_menu['_default'] = self.objclass_menu[objclass_def] def cache_schema(self, schema, uri=None): """Cache an external schema reference. schema -- JSON schema to cache uri -- Optional URI for the schema If the JSON schema for merging contains external references, they will be fetched using HTTP from their respective URLs. Alternatively, this method can be used to pre-populate the cache with any external schemas that are already known. If URI is omitted, it is obtained from the schema itself ('id' or '$id' keyword, depending on the JSON Schema draft used) """ if uri is None: if hasattr(self.validator, 'ID_OF'): uri = self.validator.ID_OF(schema) else: # jsonschema<3.0.0 uri = schema.get('id', '') self.validator.resolver.store.update(((uri, schema), )) def merge(self, base, head, meta=None): """Merge head into base. base -- Old JSON document you are merging into. head -- New JSON document for merging into base. meta -- Optional dictionary with meta-data. Any elements in the meta dictionary will be added to the dictionaries appended by the version strategies. Returns an updated base document """ schema = JSONValue(self.schema) if base is None: base = JSONValue(undef=True) else: base = JSONValue(base) head = JSONValue(head) walk = WalkInstance(self, base, head) return walk.descend(schema, base, head, meta).val def get_schema(self, meta=None): """Get JSON schema for the merged document. meta -- Optional JSON schema for the meta-data. Returns a JSON schema for documents returned by the merge() method. """ if meta is not None: # This is kind of ugly - schema for meta data # can again contain references to external schemas. # # Since we already have in place all the machinery # to resolve these references in the merge schema, # we (ab)use it here to do the same for meta data # schema. m = Merger(meta) m.validator.resolver.store.update(self.validator.resolver.store) w = WalkSchema(m) meta = w._resolve_refs(JSONValue(meta), resolve_base=True).val schema = JSONValue(self.schema) walk = WalkSchema(self) return walk.descend(schema, meta).val
ctx_val = self.get_key(walk, e.object_context, idRef) ctx_key = idRef.split("/")[-1] raise e.with_context(**{ctx_key: ctx_val}) except jsonschema.exceptions.RefResolutionError: # self.get_key failed, nothing to do but re-raise MergeCollision as is raise e.merge_collision PRISM_MERGE_STRATEGIES = { # This overwrites the default jsonmerge merge strategy for literal values. "overwrite": ThrowOnOverwrite(), # This adds context to MergeCollisions "arrayMergeById": ArrayMergeByIdWithContextForMergeCollision(), "objectMerge": ObjectMergeWithContextForMergeCollision(), # Alias the builtin jsonmerge overwrite strategy "overwriteAny": strategies.Overwrite(), } def merge_clinical_trial_metadata(patch: dict, target: dict) -> Tuple[dict, List[str]]: """ Merges two clinical trial metadata objects together Args: patch: the metadata object to add target: the existing metadata object Returns: arg1: the merged metadata object arg2: list of validation errors """
class Merger(object): STRATEGIES = { "overwrite": strategies.Overwrite(), "version": strategies.Version(), "append": strategies.Append(), "objectMerge": strategies.ObjectMerge(), "arrayMergeById": strategies.ArrayMergeById() } def __init__(self, schema, strategies=()): """Create a new Merger object. schema -- JSON schema to use when merging. strategies -- Any additional merge strategies to use during merge. strategies argument should be a dict mapping strategy names to instances of Strategy subclasses. """ self.schema = schema self.validator = Draft4Validator(schema) self.strategies = dict(self.STRATEGIES) self.strategies.update(strategies) def cache_schema(self, schema, uri=None): """Cache an external schema reference. schema -- JSON schema to cache uri -- Optional URI for the schema If the JSON schema for merging contains external references, they will be fetched using HTTP from their respective URLs. Alternatively, this method can be used to pre-populate the cache with any external schemas that are already known. If URI is omitted, it is obtained from the 'id' keyword of the schema. """ if uri is None: uri = schema.get('id', '') self.validator.resolver.store.update(((uri, schema),)) def merge(self, base, head, meta=None): """Merge head into base. base -- Old JSON document you are merging into. head -- New JSON document for merging into base. meta -- Optional dictionary with meta-data. Any elements in the meta dictionary will be added to the dictionaries appended by the version strategies. Returns an updated base document """ walk = WalkInstance(self) return walk.descend(self.schema, base, head, meta) def get_schema(self, meta=None): """Get JSON schema for the merged document. meta -- Optional JSON schema for the meta-data. Returns a JSON schema for documents returned by the merge() method. """ if meta is not None: # This is kind of ugly - schema for meta data # can again contain references to external schemas. # # Since we already have in place all the machinery # to resolve these references in the merge schema, # we (ab)use it here to do the same for meta data # schema. m = Merger(meta) m.validator.resolver.store.update(self.validator.resolver.store) w = WalkSchema(m) meta = w.resolve_refs(meta, resolve_base=True) walk = WalkSchema(self) return walk.descend(self.schema, meta)
class Merger(object): STRATEGIES = { "discard": strategies.Discard(), "overwrite": strategies.Overwrite(), "version": strategies.Version(), "append": strategies.Append(), "objectMerge": strategies.ObjectMerge(), "arrayMergeById": strategies.ArrayMergeById(), "arrayMergeByIndex": strategies.ArrayMergeByIndex(), } def __init__(self, schema, strategies=(), objclass_def='dict', objclass_menu=None, validatorclass=Draft4Validator): """Create a new Merger object. schema -- JSON schema to use when merging. strategies -- Any additional merge strategies to use during merge. objclass_def -- Name of the default class for JSON objects. objclass_menu -- Any additional classes for JSON objects. validatorclass -- JSON Schema validator class. strategies argument should be a dict mapping strategy names to instances of Strategy subclasses. objclass_def specifies the default class used for JSON objects when one is not specified in the schema. It should be 'dict' (dict built-in), 'OrderedDict' (collections.OrderedDict) or one of the names specified in the objclass_menu argument. If not specified, 'dict' is used. objclass_menu argument should be a dictionary that maps a string name to a function or class that will return an empty dictionary-like object to use as a JSON object. The function must accept either no arguments or a dictionary-like object. validatorclass argument can be used to supply a validator class from jsonschema. This can be used for example to specify which JSON Schema draft version will be used during merge. """ self.schema = schema if hasattr(validatorclass, 'ID_OF'): resolver = LocalRefResolver.from_schema(schema, id_of=validatorclass.ID_OF) else: # jsonschema<3.0.0 resolver = LocalRefResolver.from_schema(schema) self.validator = validatorclass(schema, resolver=resolver) self.strategies = dict(self.STRATEGIES) self.strategies.update(strategies) self.objclass_menu = {'dict': dict, 'OrderedDict': OrderedDict} if objclass_menu: self.objclass_menu.update(objclass_menu) self.objclass_menu['_default'] = self.objclass_menu[objclass_def] def cache_schema(self, schema, uri=None): """Cache an external schema reference. schema -- JSON schema to cache uri -- Optional URI for the schema If the JSON schema for merging contains external references, they will be fetched using HTTP from their respective URLs. Alternatively, this method can be used to pre-populate the cache with any external schemas that are already known. If URI is omitted, it is obtained from the schema itself ('id' or '$id' keyword, depending on the JSON Schema draft used) """ if uri is None: if hasattr(self.validator, 'ID_OF'): uri = self.validator.ID_OF(schema) else: # jsonschema<3.0.0 uri = schema.get('id', '') self.validator.resolver.store.update(((uri, schema), )) def merge(self, base, head, meta=None, merge_options=None): """Merge head into base. base -- Old JSON document you are merging into. head -- New JSON document for merging into base. merge_options -- Optional dictionary with merge options. Keys of merge_options must be names of the strategies. Values must be dictionaries of merge options as in the mergeOptions schema element. Options in merge_options are applied to all instances of a strategy. Values in schema override values given in merge_options. Returns an updated base document """ schema = JSONValue(self.schema) if base is None: base = JSONValue(undef=True) else: base = JSONValue(base) head = JSONValue(head) if merge_options is None: merge_options = {} # backwards compatibility jsonmerge<=1.6.0 if meta is not None: warnings.warn( "'meta' argument is deprecated. Please use " "merge_options={'version': {'metadata': ...}}.", DeprecationWarning, 2) merge_options['version'] = {'metadata': meta} walk = WalkInstance(self, base, head, merge_options) return walk.descend(schema, base, head).val def get_schema(self, meta=None, merge_options=None): """Get JSON schema for the merged document. merge_options -- Optional dictionary with merge options. Keys of merge_options must be names of the strategies. Values must be dictionaries of merge options as in the mergeOptions schema element. Options in merge_options are applied to all instances of a strategy. Values in schema override values given in merge_options. Returns a JSON schema for documents returned by the merge() method. """ if merge_options is None: merge_options = {} # backwards compatibility jsonmerge<=1.6.0 if meta is not None: warnings.warn( "'meta' argument is deprecated. Please use " "merge_options={'version': {'metadataSchema': ...}}.", DeprecationWarning, 2) merge_options['version'] = {'metadataSchema': meta} schema = JSONValue(self.schema) walk = WalkSchema(self, merge_options) return walk.descend(schema).val